[PYTHON] Ein Amateur stolperte in Deep Learning von Grund auf neu. Hinweis: Kapitel 7

Einführung

Plötzlich begann ich zu studieren "Deep Learning von Grund auf - Theorie und Implementierung von Deep Learning, das mit Python gelernt wurde". Es ist ein Memo der Reise.

Die Ausführungsumgebung ist macOS Mojave + Anaconda 2019.10 und die Python-Version ist 3.7.4. Weitere Informationen finden Sie in Kapitel 1 dieses Memos.

(Zu anderen Kapiteln dieses Memos: Kapitel 1 / Kapitel 2 / Kapitel 3 / Kapitel 4 / [Kapitel 5](https: // qiita. com / segavvy / items / 8707e4e65aa7fa357d8a) / Kapitel 6 / Kapitel 7 / Kapitel 8 / Zusammenfassung)

Kapitel 7 Faltungs-Neuronales Netz

Dieses Kapitel beschreibt das Convolution Neural Network (CNN).

7.1 Gesamtstruktur

Zusätzlich zu der vorhandenen Ebene "Affine", "Softmax" und "ReLU" wird erklärt, dass die Ebene "Faltung" und die Ebene "Pooling" angezeigt werden.

7.2 Faltschicht

Die Erklärung der Faltungsschicht ist leichter zu lesen, wenn Sie ein wenig Bildverarbeitung haben.

Darin heißt es: "Das Bild ist normalerweise eine dreidimensionale Form in vertikaler, horizontaler und Kanalrichtung." Da es sich bei dem Bild jedoch um vertikale und horizontale 2D-Daten handelt, handelt es sich nicht um 3D-Daten mit hinzugefügter Tiefe? Einige Leute mögen das denken.

"Kanal" bezieht sich hier auf Informationen für jede Farbe, wie z. B. RGB. Bei Graustufendaten (nur Schwarz- und Weißtöne) wie MNIST kann die Dichte eines Punkts durch einen Wert ausgedrückt werden, sodass ein Kanal ausreicht. In einem Farbbild ist jedoch ein Punkt Rot, Grün, Blau. Da dies durch die Dichte der drei Werte von (RGB) ausgedrückt wird, sind drei Kanäle erforderlich. Zu den Farbinformationskanälen gehören neben RGB auch CMYK, HSV und Transparent Alpha. Für Details gehen Sie zu "RGB CMYK" usw. und Sie werden viele Erklärungen finden (obwohl es viele Geschichten gibt, die dem Drucken etwas näher kommen).

Das Wort "Filter" ist ebenfalls etwas Besonderes und bezieht sich bei der Bildverarbeitung auf die Verarbeitung, die verwendet wird, um nur die erforderlichen Teile (z. B. Konturen) eines Bildes zu extrahieren oder unnötige Informationen zu entfernen. Für diejenigen, die nicht damit vertraut sind, ist es einfacher zu verstehen, wenn Sie einen Überblick über den Faltungsfilter in der Bildverarbeitung erhalten. @ t-tkd3as Ergebnisbild des 3x3-Faltungsfilters wird empfohlen, da es leicht vorstellbar ist.

Abgesehen davon scheint dieses Buch eine Regel zu sein, die keine langen Noten für Katakana mit 3 oder mehr Noten wie "Layer" hinzufügt. Da der "Filter" jedoch eine lange Notenschreibweise hat, kann es sich um eine einheitliche Auslassung handeln. Übrigens, als Microsoft 2008 die Methode der Notation von Katakana mit langen Notizen umstellte [^ 1], war ich für die Entwicklung von Paketanwendungen für Windows und für die Korrektur des Wortlauts von Programmen und Handbüchern verantwortlich. Es war schwer. Vorher war ich daran beteiligt, Kana mit halber Breite von der GUI in Windows 98 zu entfernen ... In dieser Branche ist Japanisch wirklich unpraktisch: Schweiß:

Kommen wir zurück zur Geschichte und fahren fort.

7.3 Pooling-Schicht

Was die Pooling-Schicht betrifft, hatte ich keine besonderen Stolpersteine.

7.4 Implementierung der Faltungs- / Pooling-Schicht

Die Implementierung der Faltungsschicht und der Poolschicht ist im Code kurz, aber kompliziert, da sich die Form der Zieldaten mit "im2col", "numpy.ndarray.reshape" und "numpy.ndarray.transpose" schnell ändert. Anfangs war es verwirrend, aber ich konnte es verstehen, indem ich mich auf @ daizutabis [Implementierung der Faltungs- / Pooling-Ebene "Deep Learning from Grund" (https://qiita.com/daizutabi/items/856042fb1ea9da150741) bezog. ..

Erstens ist die Implementierung der Faltungsschicht. Ich habe viele Kommentare, weil ich meinen Kopf nicht bekommen kann, wenn ich die Form nicht schreibe.

convolution.py


# coding: utf-8
import os
import sys
import numpy as np
sys.path.append(os.pardir)  #Fügen Sie dem Pfad das übergeordnete Verzeichnis hinzu
from common.util import im2col, col2im


class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        """Faltungsschicht
        
        Args:
            W (numpy.ndarray):Filter (Gewicht), Form(FN, C, FH, FW)。
            b (numpy.ndarray):Bias, Form(FN)。
            stride (int, optional):Schritt, Standard ist 1.
            pad (int, optional):Auffüllen, Standard ist 0.
        """
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        self.dW = None      #Differenzwert des Gewichts
        self.db = None      #Differenzwert der Vorspannung

        self.x = None       #Eingabe während der Vorwärtsausbreitung für die Rückwärtsausbreitung erforderlich
        self.col_x = None   #Col-Expansionsergebnis der Eingabe zum Zeitpunkt der Vorwärtsausbreitung, die für die Rückwärtsausbreitung erforderlich ist
        self.col_W = None   #Col-Expansionsergebnis des Filters zum Zeitpunkt der Vorwärtsausbreitung, die für die Rückwärtsausbreitung erforderlich ist

    def forward(self, x):
        """Vorwärtsausbreitung
        
        Args:
            x (numpy.ndarray):Eingang. Die Form ist(N, C, H, W)。
            
        Returns:
            numpy.ndarray:Ausgabe. Die Form ist(N, FN, OH, OW)。
        """
        FN, C, FH, FW = self.W.shape  # FN:Anzahl der Filter, C.:Anzahl der Kanäle, FH:Filterhöhe, FW:Breite
        N, x_C, H, W = x.shape        # N:Chargengröße, x_C:Anzahl der Kanäle, H: Höhe der Eingabedaten, W.:Breite
        assert C == x_C, f'Nicht übereinstimmende Anzahl der Kanäle![C]{C}, [x_C]{x_C}'

        #Berechnung der Ausgabegröße
        assert (H + 2 * self.pad - FH) % self.stride == 0, 'OH ist nicht teilbar!'
        assert (W + 2 * self.pad - FW) % self.stride == 0, 'OW ist unteilbar!'
        OH = int((H + 2 * self.pad - FH) / self.stride + 1)
        OW = int((W + 2 * self.pad - FW) / self.stride + 1)

        #Erweitern Sie die Eingabedaten
        # (N, C, H, W) → (N * OH * OW, C * FH * FW)
        col_x = im2col(x, FH, FW, self.stride, self.pad)

        #Filter erweitern
        # (FN, C, FH, FW) → (C * FH * FW, FN)
        col_W = self.W.reshape(FN, -1).T

        #Ausgabe berechnen (Spalte_x, col_W,Die Berechnung für b ist genau die gleiche wie für die affine Schicht.
        # (N * OH * OW, C * FH * FW)・(C * FH * FW, FN) → (N * OH * OW, FN)
        out = np.dot(col_x, col_W) + self.b

        #Formatieren des Ergebnisses
        # (N * OH * OW, FN) → (N, OH, OW, FN) → (N, FN, OH, OW)
        out = out.reshape(N, OH, OW, FN).transpose(0, 3, 1, 2)

        #Für Backpropagation speichern
        self.x = x
        self.col_x = col_x
        self.col_W = col_W

        return out

    def backward(self, dout):
        """Backpropagation
        
        Args:
            dout (numpy.ndarray):Der Differenzwert und die Form, die von der rechten Schicht übertragen werden(N, FN, OH, OW)。
        
        Returns:
            numpy.ndarray:Differenzierungswert (Gradient), Form(N, C, H, W)。
        """
        FN, C, FH, FW = self.W.shape  #Die Form des Differenzwerts ist dieselbe wie W.(FN, C, FH, FW)

        #Erweitern Sie den Differenzwert aus der rechten Ebene
        # (N, FN, OH, OW) → (N, OH, OW, FN) → (N * OH * OW, FN)
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        #Differenzwertberechnung (Spalte_x, col_W,Die Berechnung für b ist genau die gleiche wie für die affine Schicht.
        dcol_x = np.dot(dout, self.col_W.T)     # → (N * OH * OW, C * FH * FW)
        self.dW = np.dot(self.col_x.T, dout)    # → (C * FH * FW, FN)
        self.db = np.sum(dout, axis=0)          # → (FN)

        #Formatieren des Differenzwerts des Filters (Gewicht)
        # (C * FH * FW, FN) → (FN, C * FH * FW) → (FN, C, FH, FW)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        #Formatieren des Ergebnisses (Farbverlauf)
        # (N * OH * OW, C * FH * FW) → (N, C, H, W)
        dx = col2im(dcol_x, self.x.shape, FH, FW, self.stride, self.pad)
    
        return dx

Als nächstes folgt die Implementierung der Pooling-Schicht. Dies ist auch voller Kommentare.

pooling.py


# coding: utf-8
import os
import sys
import numpy as np
sys.path.append(os.pardir)  #Fügen Sie dem Pfad das übergeordnete Verzeichnis hinzu
from common.util import im2col, col2im


class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        """Pooling-Schicht
        
        Args:
            pool_h (int):Poolbereichshöhe
            pool_w (int):Breite des Poolbereichs
            stride (int, optional):Schritt, Standard ist 1.
            pad (int, optional):Auffüllen, Standard ist 0.
        """
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None           #Eingabe während der Vorwärtsausbreitung für die Rückwärtsausbreitung erforderlich
        self.arg_max = None     #Die für die Vorwärtsausbreitung verwendete Spalte, die für die Rückwärtsausbreitung erforderlich ist_x Position jeder Reihe

    def forward(self, x):
        """Vorwärtsausbreitung
        
        Args:
            x (numpy.ndarray):Eingabe, Form(N, C, H, W)。
            
        Returns:
            numpy.ndarray:Ausgabe, Form(N, C, OH, OW)。
        """
        N, C, H, W = x.shape  # N:Anzahl der Daten, C.:Anzahl der Kanäle, H.:Höhe, W.:Breite

        #Berechnung der Ausgabegröße
        assert (H - self.pool_h) % self.stride == 0, 'OH ist nicht teilbar!'
        assert (W - self.pool_w) % self.stride == 0, 'OW ist unteilbar!'
        OH = int((H - self.pool_h) / self.stride + 1)
        OW = int((W - self.pool_w) / self.stride + 1)

        #Erweitern und formatieren Sie die Eingabedaten
        # (N, C, H, W) → (N * OH * OW, C * PH * PW)
        col_x = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        # (N * OH * OW, C * PH * PW) → (N * OH * OW * C, PH * PW)
        col_x = col_x.reshape(-1, self.pool_h * self.pool_w)

        #Ausgabe berechnen
        # (N * OH * OW * C, PH * PW) → (N * OH * OW * C)
        out = np.max(col_x, axis=1)

        #Formatieren des Ergebnisses
        # (N * OH * OW * C) → (N, OH, OW, C) → (N, C, OH, OW)
        out = out.reshape(N, OH, OW, C).transpose(0, 3, 1, 2)

        #Für Backpropagation speichern
        self.x = x
        self.arg_max = np.argmax(col_x, axis=1)  # col_x Maximale Position (Index) jeder Zeile

        return out

    def backward(self, dout):
        """Backpropagation
        
        Args:
            dout (numpy.ndarray):Der Differenzwert und die Form, die von der rechten Schicht übertragen werden(N, C, OH, OW)。
        
        Returns:
            numpy.ndarray:Differenzierungswert (Gradient), Form(N, C, H, W)。
        """
        #Formen Sie den Differenzwert aus der rechten Ebene
        # (N, C, OH, OW) → (N, OH, OW, C)
        dout = dout.transpose(0, 2, 3, 1)

        #Initialisieren Sie col für den resultierenden Differenzwert mit 0
        # (N * OH * OW * C, PH * PW)
        pool_size = self.pool_h * self.pool_w
        dcol_x = np.zeros((dout.size, pool_size))

        #Stellen Sie den Differenzwert von dout (= dout manma) nur an der Position ein, die während der Vorwärtsausbreitung als Maximalwert angenommen wurde.
        #Die Position des Werts, der während der Vorwärtsausbreitung nicht übernommen wurde, bleibt bei der Initialisierung 0.
        #(Entspricht der Verarbeitung, wenn x in ReLU größer als 0 und x kleiner als 0 ist.)
        assert dout.size == self.arg_max.size, 'Col während der Vorwärtsausbreitung_Entspricht nicht der Anzahl der Zeilen in x'
        dcol_x[np.arange(self.arg_max.size), self.arg_max.flatten()] = \
            dout.flatten()

        #Formatieren des resultierenden Differenzwerts 1
        # (N * OH * OW * C, PH * PW) → (N, OH, OW, C, PH * PW)
        dcol_x = dcol_x.reshape(dout.shape + (pool_size,))  #Letzte','Zeigt einen Ein-Element-Taple an

        #Formatieren des resultierenden Differenzwerts 2
        # (N, OH, OW, C, PH * PW) → (N * OH * OW, C * PH * PW)
        dcol_x = dcol_x.reshape(
            dcol_x.shape[0] * dcol_x.shape[1] * dcol_x.shape[2], -1
        )

        #Formatieren des resultierenden Differenzwerts 3
        # (N * OH * OW, C * PH * PW) → (N, C, H, W)
        dx = col2im(
            dcol_x, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad
        )

        return dx

7.5 CNN-Implementierung

Implementieren Sie CNN, indem Sie die vorherigen Implementierungen kombinieren.

(1) Implementierung jeder Schicht

Lassen Sie uns zunächst die Ein- und Ausgabe in diesem Netzwerk organisieren.

Schicht Eingabe- / Ausgabeform Form zum Zeitpunkt der Montage
$ (Chargengröße N.,Anzahl der Kanäle CH,Bildhöhe H.,Breite W.) $ $ (100, 1, 28, 28) $
:one: Convolution
$ (Chargengröße N.,Anzahl der Filter FN,Ausgangshöhe OH,Breite OW) $ $ (100, 30, 24, 24) $
:two: ReLU
$ (Chargengröße N.,Anzahl der Filter FN,Ausgangshöhe OH,Breite OW) $ $ (100, 30, 24, 24) $
:three: Pooling
$ (Chargengröße N.,Anzahl der Filter FN,Ausgangshöhe OH,Breite OW) $ $ (100, 30, 12, 12) $
:four: Affine
$ (Chargengröße N.,Versteckte Ebenengröße) $ $ (100, 100) $
:five: ReLU
$ (Chargengröße N.,Versteckte Ebenengröße) $ $ (100, 100) $
:six: Affine
$ (Chargengröße N.,Endgültige Ausgabegröße) $ $ (100, 10) $
:seven: Softmax
$ (Chargengröße N.,Endgültige Ausgabegröße) $ $ (100, 10) $

Die Implementierung der Faltungsschicht und der Poolschicht ist wie oben beschrieben.

Die affine Ebene erfordert einige Änderungen an der vorherigen Implementierung. Zuvor [5.6.2 Batch Affine Layer](https://qiita.com/segavvy/items/8707e4e65aa7fa357d8a#562-%E3%83%90%E3%83%83%E3%83%81%E7%89% Bei der Implementierung mit 88affine% E3% 83% AC% E3% 82% A4% E3% 83% A4) war die Eingabe zweidimensional ($ Stapelgröße N $, Bildgröße), diesmal jedoch die vierte Die Eingabe der affinen Ebene ist 4 Dimensionen (Anzahl der Stapel $ N $, Anzahl der Filter $ FN $, Pooling-Ergebnis $ OH $, $ OW $), daher ist es notwendig, damit umzugehen. Auf Seite 152 des Buches gibt es die Maßgabe, dass "Die Implementierung von Affine in common / layer.py eine Implementierung ist, die den Fall berücksichtigt, in dem die Eingabedaten Tensor (4D-Daten) sind". Ich wusste nicht, ob es unbeaufsichtigt blieb, aber diesmal sollte es verwendet werden.

Das Folgende ist eine Implementierung der affinen Ebene, die auch die Eingabe von 3 Dimensionen oder mehr unterstützt.

affine.py


# coding: utf-8
import numpy as np


class Affine:

    def __init__(self, W, b):
        """Affine Schicht
        
        Args:
            W (numpy.ndarray):Gewicht
            b (numpy.ndarray):vorspannen
        """
        self.W = W                      #Gewicht
        self.b = b                      #vorspannen
        self.x = None                   #Eingabe (nach 2D)
        self.dW = None                  #Differenzwert des Gewichts
        self.db = None                  #Differenzwert der Vorspannung
        self.original_x_shape = None    #Ursprüngliche Eingabeform (für Eingabe von 3 Dimensionen oder mehr)

    def forward(self, x):
        """Vorwärtsausbreitung
        
        Args:
            x (numpy.ndarray):Eingang
            
        Returns:
            numpy.ndarray:Ausgabe
        """
        #Zweidimensionale Eingabe von drei oder mehr Dimensionen (Tensor)
        self.original_x_shape = x.shape  #Weil es notwendig ist, die Form zu speichern und durch Rückausbreitung wiederherzustellen
        x = x.reshape(x.shape[0], -1)
        self.x = x

        #Ausgabe berechnen
        out = np.dot(x, self.W) + self.b

        return out

    def backward(self, dout):
        """Backpropagation
        
        Args:
            dout (numpy.ndarray):Differenzierungswert von der rechten Schicht übertragen

        Returns:
            numpy.ndarray:Differenzierungswert
        """
        #Differenzwertberechnung
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        #Kehren Sie zur ursprünglichen Form zurück
        dx = dx.reshape(*self.original_x_shape)
        return dx

Die Ebenen ReLU und Softmax sind dieselben wie in der vorherigen Implementierung, werden jedoch erneut gedruckt.

relu.py


# coding: utf-8


class ReLU:
    def __init__(self):
        """ReLU-Schicht
        """
        self.mask = None

    def forward(self, x):
        """Vorwärtsausbreitung
        
        Args:
            x (numpy.ndarray):Eingang
            
        Returns:
            numpy.ndarray:Ausgabe
        """
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0

        return out

    def backward(self, dout):
        """Backpropagation
        
        Args:
            dout (numpy.ndarray):Differenzierungswert von der rechten Schicht übertragen
        
        Returns:
            numpy.ndarray:Differenzierungswert
        """
        dout[self.mask] = 0
        dx = dout

        return dx

softmax_with_loss.py


# coding: utf-8
from functions import softmax, cross_entropy_error


class SoftmaxWithLoss:
    def __init__(self):
        """Softmax-with-Verlustschicht
        """
        self.loss = None    #Verlust
        self.y = None       #Ausgabe von Softmax
        self.t = None       #Lehrerdaten (eins-hot vector)

    def forward(self, x, t):
        """Vorwärtsausbreitung
        
        Args:
            x (numpy.ndarray):Eingang
            t (numpy.ndarray):Lehrerdaten

        Returns:
            float:Kreuzentropiefehler
        """
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)

        return self.loss

    def backward(self, dout=1):
        """Backpropagation
        
        Args:
            dout (float, optional):Der von der rechten Schicht übertragene Differenzwert. Der Standardwert ist 1.

        Returns:
            numpy.ndarray:Differenzierungswert
        """
        batch_size = self.t.shape[0]    #Anzahl der Chargen
        dx = (self.y - self.t) * (dout / batch_size)

        return dx

Die zum Implementieren der Softmax-Schicht erforderlichen Funktionen werden ebenfalls wie zuvor neu gedruckt. Außerdem werden die Funktionen, die diesmal nicht verwendet werden, gelöscht.

functions.py


# coding: utf-8
import numpy as np


def softmax(x):
    """Softmax-Funktion
    
    Args:
        x (numpy.ndarray):Eingang
    
    Returns:
        numpy.ndarray:Ausgabe
    """
    #Für die Stapelverarbeitung ist x(Anzahl der Chargen, 10)Es wird eine zweidimensionale Anordnung von.
    #In diesem Fall ist es erforderlich, für jedes Bild, das gesendet wird, eine gute Berechnung durchzuführen.
    #Hier np, damit es in 1 und 2 Dimensionen geteilt werden kann..max()Und np.sum()Ist Achse=-Berechnet mit 1
    #Keepdims, damit es so gesendet werden kann, wie es ist=True, um die Dimension beizubehalten.
    c = np.max(x, axis=-1, keepdims=True)
    exp_a = np.exp(x - c)  #Überlaufmaßnahmen
    sum_exp_a = np.sum(exp_a, axis=-1, keepdims=True)
    y = exp_a / sum_exp_a
    return y


def cross_entropy_error(y, t):
    """Berechnung des Kreuzentropiefehlers
    
    Args:
        y (numpy.ndarray):Neuronale Netzwerkausgabe
        t (numpy.ndarray):Richtiges Antwortetikett
    
    Returns:
        float:Kreuzentropiefehler
    """

    #Wenn es Daten gibt, formen Sie sie (machen Sie eine Datenzeile)
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    #Berechnen Sie den Fehler und normalisieren Sie ihn anhand der Anzahl der Chargen
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size

(2) Implementierung des Optimierers

Informationen zum Optimierer zur Optimierung der Parameter finden Sie unter 6.1 Parameter aktualisieren (https://qiita.com/segavvy/items/ca4ac4c9ee1a126bff41#61-%E3%83%91%E3%83%A9%E3%83%) A1% E3% 83% BC% E3% 82% BF% E3% 81% AE% E6% 9B% B4% E6% 96% B0) Ich habe die Implementierung nur durch Lesen übersprungen und mich dieses Mal für AdaGrad entschieden Ich habe versucht zu implementieren. Es ist fast das gleiche wie der Code im Buch.

ada_grad.py


# coding: utf-8
import numpy as np


class AdaGrad:

    def __init__(self, lr=0.01):
        """Parameteroptimierung mit AdaGrad
        
        Args:
            lr (float, optional):Lernkoeffizient, Standard ist 0.01。
        """
        self.lr = lr
        self.h = None   #Summe der Quadrate des bisherigen Gradienten

    def update(self, params, grads):
        """Parameteraktualisierung
        
        Args:
            params (dict):Das Wörterbuch der zu aktualisierenden Parameter ist der Schlüssel'W1'、'b1'Eine solche.
            grads (dict):Verlaufswörterbuch entsprechend params
        """

        #Initialisierung von h
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        #aktualisieren
        for key in params.keys():

            #Update von h
            self.h[key] += grads[key] ** 2

            #Parameteraktualisierung, letzte 1e-7 vermeidet die Division durch 0
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

(3) Implementierung von CNN

CNN zuvor [5.7.2 Implementieren eines neuronalen Netzwerks zur Backpropagation von Fehlern](https://qiita.com/segavvy/items/8707e4e65aa7fa357d8a#572-%E8%AA%A4%E5%B7%AE% E9% 80% 86% E4% BC% 9D% E6% 92% AD% E6% B3% 95% E3% 81% AB% E5% AF% BE% E5% BF% 9C% E3% 81% 97% E3% 81% 9F% E3% 83% 8B% E3% 83% A5% E3% 83% BC% E3% 83% A9% E3% 83% AB% E3% 83% 8D% E3% 83% 83% E3% 83% 88% E3% 83% AF% E3% 83% BC% E3% 82% AF% E3% 81% AE% E5% AE% 9F% E8% A3% 85) Basierend auf dem "TwoLayerNet" Ich habe es gemäß den Anweisungen implementiert.

Der Code im Buch verwendet "OrderedDict", aber wie beim letzten Mal verwenden wir hier "normales Dikt". Dies liegt daran, dass ab Python 3.7 die Einfügereihenfolge von dict-Objekten gespeichert wird [^ 2]. Außerdem bin ich auf die Implementierung von "Genauigkeit" gestoßen, daher werde ich es später erklären.

Nachfolgend finden Sie die Implementierung von CNN.

simple_conv_net.py


# coding: utf-8
import numpy as np
from affine import Affine
from convolution import Convolution
from pooling import Pooling
from relu import ReLU
from softmax_with_loss import SoftmaxWithLoss


class SimpleConvNet:

    def __init__(
        self, input_dim=(1, 28, 28),
        conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
        hidden_size=100, output_size=10, weight_init_std=0.01
    ):
        """Einfaches Faltungsnetzwerk
        
        Args:
            input_dim (tuple, optional):Eingabedatenform, Standard(1, 28, 28)。
            conv_param (dict, optional):Hyperparameter der Faltungsschicht,
Der Standardwert ist{'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1}。
            hidden_size (int, optional):Die Anzahl der Neuronen in der verborgenen Schicht beträgt standardmäßig 100.
            output_size (int, optional):Anzahl der Neuronen in der Ausgabeschicht, Standard 10.
            weight_init_std (float, optional):Einstellparameter des Anfangswertes des Gewichts. Der Standardwert ist 0.01。
        """
        #Extrahieren Sie die Hyperparameter der Faltungsschicht
        filter_num = conv_param['filter_num']       #Anzahl der Filter
        filter_size = conv_param['filter_size']     #Filtergröße (gleiche Höhe und Breite)
        filter_stride = conv_param['stride']        #schreiten
        filter_pad = conv_param['pad']              #Polsterung
        
        #Die Hyperparameter der Pooling-Schicht sind festgelegt
        pool_size = 2                               #Größe (gleiche Höhe und Breite)
        pool_stride = 2                             #schreiten
        pool_pad = 0                                #Polsterung

        #Berechnung der Eingabedatengröße
        input_ch = input_dim[0]                     #Anzahl der Eingangsdatenkanäle
        assert input_dim[1] == input_dim[2], 'Eingabedaten haben vermutlich die gleiche Höhe und Breite!'
        input_size = input_dim[1]                   #Eingabedatengröße
        
        #Berechnung der Ausgangsgröße der Faltungsschicht
        assert (input_size + 2 * filter_pad - filter_size) \
            % filter_stride == 0, 'Die Ausgabegröße der Faltschicht ist nicht teilbar!'
        conv_output_size = int(
            (input_size + 2 * filter_pad - filter_size) / filter_stride + 1
        )

        #Berechnung der Ausgabegröße der Poolebene
        assert (conv_output_size - pool_size) % pool_stride == 0, \
            'Die Ausgabegröße der Poolebene ist nicht teilbar!'
        pool_output_size_one = int(
            (conv_output_size - pool_size) / pool_stride + 1  #Höhe / Breite Größe
        )
        pool_output_size = filter_num * \
            pool_output_size_one * pool_output_size_one     #Gesamtgröße aller Filter

        #Gewichtsinitialisierung
        self.params = {}
        #Faltschicht
        self.params['W1'] = weight_init_std * \
            np.random.randn(filter_num, input_ch, filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        #Affine Schicht 1
        self.params['W2'] = weight_init_std * \
            np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        #Affine Schicht 2
        self.params['W3'] = weight_init_std * \
            np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)
            
        #Schichterzeugung
        self.layers = {}    # Python 3.OrderedDict ist nicht erforderlich, da die Speicherreihenfolge des Wörterbuchs von 7 beibehalten wird
        #Faltschicht
        self.layers['Conv1'] = Convolution(
            self.params['W1'], self.params['b1'], filter_stride, filter_pad
        )
        self.layers['Relu1'] = ReLU()
        self.layers['Pool1'] = Pooling(
            pool_size, pool_size, pool_stride, pool_pad
        )
        #Affine Schicht 1
        self.layers['Affine1'] = \
            Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = ReLU()
        #Affine Schicht 2
        self.layers['Affine2'] = \
            Affine(self.params['W3'], self.params['b3'])
    
        self.lastLayer = SoftmaxWithLoss()

    def predict(self, x):
        """Inferenz durch neuronales Netzwerk
        
        Args:
            x (numpy.ndarray):Eingabe in das neuronale Netzwerk
        
        Returns:
            numpy.ndarray:Neuronale Netzwerkausgabe
        """
        #Schichten nach vorne ausbreiten
        for layer in self.layers.values():
            x = layer.forward(x)

        return x

    def loss(self, x, t):
        """Berechnung des Verlustfunktionswerts
        
        Args:
            x (numpy.ndarray):Eingabe in das neuronale Netzwerk
            t (numpy.ndarray):Richtiges Antwortetikett

        Returns:
            float:Wert der Verlustfunktion
        """
        #Inferenz
        y = self.predict(x)

        # Softmax-with-Berechnet durch Vorwärtsausbreitung der Verlustschicht
        loss = self.lastLayer.forward(y, t)

        return loss

    def accuracy(self, x, t, batch_size=100):
        """Berechnung der Erkennungsgenauigkeit
        batch_Größe ist die Chargengröße zum Zeitpunkt der Berechnung. Beim Versuch, eine große Datenmenge gleichzeitig zu berechnen
Weil im2col zu viel Speicher verbraucht und es zu Schrägstrichen kommt und es nicht funktioniert
Um das zu vermeiden.

        Args:
            x (numpy.ndarray):Eingabe in das neuronale Netzwerk
            t (numpy.ndarray):Richtiges Antwortetikett (eins-hot)
            batch_size (int), optional):Die Stapelgröße zum Zeitpunkt der Berechnung beträgt standardmäßig 100.
        
        Returns:
            float:Erkennungsgenauigkeit
        """
        #Berechnung der Anzahl der Abteilungen
        batch_num = max(int(x.shape[0] / batch_size), 1)

        #Teilt
        x_list = np.array_split(x, batch_num, 0)
        t_list = np.array_split(t, batch_num, 0)

        #Prozess in geteilten Einheiten
        correct_num = 0  #Gesamtzahl der richtigen Antworten
        for (sub_x, sub_t) in zip(x_list, t_list):
            assert sub_x.shape[0] == sub_t.shape[0], 'Hat sich die Teilungsgrenze verschoben?'
            y = self.predict(sub_x)
            y = np.argmax(y, axis=1)
            t = np.argmax(sub_t, axis=1)
            correct_num += np.sum(y == t)
        
        #Berechnung der Erkennungsgenauigkeit
        return correct_num / x.shape[0]

    def gradient(self, x, t):
        """Berechnen Sie den Gradienten für den Gewichtungsparameter mit der Methode der Fehlerrückübertragung
        
         Args:
            x (numpy.ndarray):Eingabe in das neuronale Netzwerk
            t (numpy.ndarray):Richtiges Antwortetikett
        
        Returns:
            dictionary:Ein Wörterbuch, in dem Farbverläufe gespeichert werden
        """
        #Vorwärtsausbreitung
        self.loss(x, t)     #Weitergeben, um den Verlustwert zu berechnen

        #Backpropagation
        dout = self.lastLayer.backward()
        for layer in reversed(list(self.layers.values())):
            dout = layer.backward(dout)

        #Extrahieren Sie den Differenzwert jeder Schicht
        grads = {}
        grads['W1'] = self.layers['Conv1'].dW
        grads['b1'] = self.layers['Conv1'].db
        grads['W2'] = self.layers['Affine1'].dW
        grads['b2'] = self.layers['Affine1'].db
        grads['W3'] = self.layers['Affine2'].dW
        grads['b3'] = self.layers['Affine2'].db

        return grads

Was auf diese Implementierung gestoßen ist, war "Genauigkeit", die im Buch weggelassen wird.

Während des Lernens wird die Erkennungsgenauigkeit in Einheiten von 1 Epoche berechnet. In dem in Kapitel 4 geschriebenen Code wurden jedoch sofort 60.000 Trainingsdaten eingegeben, um die Erkennungsgenauigkeit zu erhalten. Wenn ich diesmal dasselbe mache, scheint das Erweitern von "im2col" viel Speicher zu verbrauchen, und meine VM mit 4 GB Speicher stoppt beim Schrägstrich [^ 3]: Schweiß:

Die Buchquelle verbraucht jedoch immer noch weniger Speicher und funktioniert in meiner Umgebung normal. Es ist seltsam, also als ich der Quelle folgte, wurde sie geteilt und intern verarbeitet. Deshalb imitiere und teile ich es auch intern. Verwenden Sie außerdem numpy.array_split, um die Aufteilung zu implementieren. Ich tat.

(4) Umsetzung des Lernens

Das Lernen ist das vorherige [5.7.4 Lernen unter Verwendung der Fehlerrückvermehrungsmethode](https://qiita.com/segavvy/items/8707e4e65aa7fa357d8a#574-%E8%AA%A4%E5%B7%AE%E9% 80% 86% E4% BC% 9D% E6% 92% AD% E6% B3% 95% E3% 82% 92% E4% BD% BF% E3% 81% A3% E3% 81% 9F% E5% AD% Implementiert basierend auf A6% E7% BF% 92). Nachfolgend einige Punkte.

Nachfolgend finden Sie die Implementierung des Lernens.

mnist.py


# coding: utf-8
import os
import sys
import matplotlib.pylab as plt
import numpy as np
from ada_grad import AdaGrad
from simple_conv_net import SimpleConvNet
sys.path.append(os.pardir)  #Fügen Sie dem Pfad das übergeordnete Verzeichnis hinzu
from dataset.mnist import load_mnist


#Lesen Sie die MNIST-Trainingsdaten und Testdaten
(x_train, t_train), (x_test, t_test) = \
    load_mnist(normalize=True, flatten=False, one_hot_label=True)

#Hyper-Parametereinstellungen
iters_num = 6000        #Anzahl der Updates
batch_size = 100        #Chargengröße
learning_rate = 0.06    #Angenommen, Lernrate, AdaGrad

train_size = x_train.shape[0]  #Größe der Trainingsdaten
iter_per_epoch = max(int(train_size / batch_size), 1)    #Anzahl der Iterationen pro Epoche

#Einfache Erzeugung eines Faltungsnetzwerks
network = SimpleConvNet(
    input_dim=(1, 28, 28),
    conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
    hidden_size=100, output_size=10, weight_init_std=0.01
)

#Optimierungsgenerierung
optimizer = AdaGrad(learning_rate)   # AdaGrad

#Bestätigung der Erkennungsgenauigkeit vor dem Lernen
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_loss_list = []            #Speicherort des Übergangs des Wertes der Verlustfunktion
train_acc_list = [train_acc]    #Speicherort von Änderungen der Erkennungsgenauigkeit für Trainingsdaten
test_acc_list = [test_acc]      #Speicherziel des Übergangs der Erkennungsgenauigkeit für Testdaten
print(f'Vor dem Lernen[Erkennungsgenauigkeit von Trainingsdaten]{train_acc:.4f} [Erkennungsgenauigkeit von Testdaten]{test_acc:.4f}')

#Fang an zu lernen
for i in range(iters_num):

    #Mini-Batch-Generierung
    batch_mask = np.random.choice(train_size, batch_size, replace=False)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    #Gradientenberechnung
    grads = network.gradient(x_batch, t_batch)

    #Aktualisierung der Gewichtsparameter
    optimizer.update(network.params, grads)
    
    #Berechnung des Verlustfunktionswerts
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)

    #Berechnung der Erkennungsgenauigkeit für jede Epoche
    if (i + 1) % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        #Fortschrittsanzeige
        print(
            f'[Epoche]{(i + 1) // iter_per_epoch:>2} '
            f'[Anzahl der Updates]{i + 1:>5} [Wert der Verlustfunktion]{loss:.4f} '
            f'[Erkennungsgenauigkeit von Trainingsdaten]{train_acc:.4f} [Erkennungsgenauigkeit von Testdaten]{test_acc:.4f}'
        )

#Zeichnen Sie den Übergang des Wertes der Verlustfunktion
x = np.arange(len(train_loss_list))
plt.plot(x, train_loss_list, label='loss')
plt.xlabel('iteration')
plt.ylabel('loss')
plt.xlim(left=0)
plt.ylim(0, 2.5)
plt.show()

#Zeichnen Sie den Übergang der Erkennungsgenauigkeit von Trainingsdaten und Testdaten
x2 = np.arange(len(train_acc_list))
plt.plot(x2, train_acc_list, label='train acc')
plt.plot(x2, test_acc_list, label='test acc', linestyle='--')
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.xlim(left=0)
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

(5) Ausführungsergebnis

Unten sind die Ausführungsergebnisse. In meiner Umgebung dauerte es ungefähr eine Stunde.

Vor dem Lernen[Erkennungsgenauigkeit von Trainingsdaten]0.0909 [Erkennungsgenauigkeit von Testdaten]0.0909
[Epoche] 1 [Anzahl der Updates]  600 [Wert der Verlustfunktion]0.0699 [Erkennungsgenauigkeit von Trainingsdaten]0.9784 [Erkennungsgenauigkeit von Testdaten]0.9780
[Epoche] 2 [Anzahl der Updates] 1200 [Wert der Verlustfunktion]0.0400 [Erkennungsgenauigkeit von Trainingsdaten]0.9844 [Erkennungsgenauigkeit von Testdaten]0.9810
[Epoche] 3 [Anzahl der Updates] 1800 [Wert der Verlustfunktion]0.0362 [Erkennungsgenauigkeit von Trainingsdaten]0.9885 [Erkennungsgenauigkeit von Testdaten]0.9853
[Epoche] 4 [Anzahl der Updates] 2400 [Wert der Verlustfunktion]0.0088 [Erkennungsgenauigkeit von Trainingsdaten]0.9907 [Erkennungsgenauigkeit von Testdaten]0.9844
[Epoche] 5 [Anzahl der Updates] 3000 [Wert der Verlustfunktion]0.0052 [Erkennungsgenauigkeit von Trainingsdaten]0.9926 [Erkennungsgenauigkeit von Testdaten]0.9851
[Epoche] 6 [Anzahl der Updates] 3600 [Wert der Verlustfunktion]0.0089 [Erkennungsgenauigkeit von Trainingsdaten]0.9932 [Erkennungsgenauigkeit von Testdaten]0.9850
[Epoche] 7 [Anzahl der Updates] 4200 [Wert der Verlustfunktion]0.0029 [Erkennungsgenauigkeit von Trainingsdaten]0.9944 [Erkennungsgenauigkeit von Testdaten]0.9865
[Epoche] 8 [Anzahl der Updates] 4800 [Wert der Verlustfunktion]0.0023 [Erkennungsgenauigkeit von Trainingsdaten]0.9954 [Erkennungsgenauigkeit von Testdaten]0.9873
[Epoche] 9 [Anzahl der Updates] 5400 [Wert der Verlustfunktion]0.0051 [Erkennungsgenauigkeit von Trainingsdaten]0.9959 [Erkennungsgenauigkeit von Testdaten]0.9860
[Epoche]10 [Anzahl der Updates] 6000 [Wert der Verlustfunktion]0.0037 [Erkennungsgenauigkeit von Trainingsdaten]0.9972 [Erkennungsgenauigkeit von Testdaten]0.9860

スクリーンショット 2020-02-11 17.03.04.png スクリーンショット 2020-02-11 17.03.20.png Infolgedessen betrug die Erkennungsgenauigkeit der Trainingsdaten 99,72% und die Erkennungsgenauigkeit der Testdaten 98,60%. Mit einer Epoche hat es bereits die vorherige Erkennungsgenauigkeit überschritten. Da sich die Erkennungsgenauigkeit der Testdaten seit etwa 7 Epochen nicht geändert hat, wurde sie danach möglicherweise nur noch zu viel gelernt. Trotzdem ist die Genauigkeit von 98,60% mit einem einfachen CNN erstaunlich.

Ich habe auch versucht, die Quelle des Buches zu verwenden, aber aus irgendeinem Grund ist die Berechnung der Erkennungsgenauigkeit für jede Epoche sehr schnell. Auf mysteriöse Weise stellte ich fest, dass es möglich war, mit dem Parameter "evalu_sample_num_per_epoch" der Klasse "Trainer" zu probieren, und sowohl das Trainingsbild als auch das Testbild wurden nur mit den ersten 1.000 Bildern berechnet. unfair! : ungerührt:

7.6 CNN-Visualisierung

Es ist erstaunlich, dass die erforderlichen Filter wie Kanten- und Blob-Extraktion automatisch erstellt werden. Es ist sehr interessant, dass der Abstraktionsgrad mit der Schichtung der Schichten zunimmt.

7.7 Typisches CNN

Es wird gesagt, dass Big Data und GPU einen großen Beitrag zur Entwicklung von Deep Learning leisten, aber ich denke, dass die Verbreitung der Cloud, die es ermöglicht, riesige Maschinenressourcen zu geringen Kosten zu nutzen, ebenfalls ein wichtiger Punkt ist.

Es ist auch eine völlige Seite, aber ich war zutiefst bewegt von der Tatsache, dass LeNets Vorschlag 1998, vor 20 Jahren, und 1998 ein neuerer Eindruck war. Ich will nicht alt werden: Schweiß:

7.8 Zusammenfassung

Es war etwas schwierig zu implementieren, aber dank dessen verstand ich CNN. Das ist alles für dieses Kapitel. Wenn Sie Fehler haben, wäre ich Ihnen dankbar, wenn Sie darauf hinweisen könnten.

(Zu anderen Kapiteln dieses Memos: Kapitel 1 / Kapitel 2 / Kapitel 3 / Kapitel 4 / [Kapitel 5](https: // qiita. com / segavvy / items / 8707e4e65aa7fa357d8a) / Kapitel 6 / Kapitel 7 / Kapitel 8 / Zusammenfassung)

[^ 1]: [Änderungen an der langen Notiz am Ende von Fremdwörtern und Katakana-Begriffen in Microsoft-Produkten und -Diensten](https://web.archive.org/web/20130228002415/http://www.microsoft.com/japan/ presspass / detail.aspx? newsid = 3491) (* Da zu diesem Zeitpunkt keine Seiten mehr vorhanden sind, Wikipedia> Lange Notizen B3% E7% AC% A6) ist auch ein Link zur Wayback-Maschine des Internet-Archivs)

[^ 2]: Siehe "Verbesserung des Python-Datenmodells" in Was ist neu in Python 3.7.

[^ 3]: Slashing ist ein Phänomen, das auftritt, wenn der Speicher nicht ausreicht, und es ist problematisch, da es für jedes Betriebssystem nicht mehr funktionsfähig sein kann. Wenn Sie an der Speicherverwaltung des Betriebssystems interessiert sind, lesen Sie bitte die zuvor veröffentlichte Einführung in die Speicherverwaltung für alle: 01! : grinsen:

Recommended Posts

Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 1
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 in Deep Learning von Grund auf neu Hinweis: Kapitel 4
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 2
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 über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 1
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 4
[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 6]
"Deep Learning von Grund auf neu" mit Haskell (unvollendet)
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
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
Deep Learning von Grund auf neu Kapitel 2 Perceptron (Memo lesen)
Deep Learning von Grund auf 1-3 Kapitel
Deep Learning / Deep Learning von Grund auf neu Kapitel 3 Memo
Deep Learning / Deep Learning von Null 2 Kapitel 5 Memo
Erstellen Sie mit Docker eine Umgebung für "Deep Learning von Grund auf neu"
Tiefes Lernen von Grund auf neu (Kostenberechnung)
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] Ich habe versucht, die Gradientenbestätigung auf leicht verständliche Weise zu erklären.
"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
Warum ModuleNotFoundError: In "Deep Learning from Grund" wird kein Modul mit dem Namen "didaset.mnist" angezeigt.
Schreiben Sie Ihre Eindrücke von der Deep Learning 3 Framework Edition, die von Grund auf neu erstellt wurde
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
Tiefes Lernen / Tiefes Lernen von Grund auf 2-Versuchen Sie, GRU zu bewegen
[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
"Deep Learning from Grund" Memo zum Selbststudium (Teil 12) Deep Learning
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
Selbststudien-Memo "Deep Learning from Grund" (unlesbares Glossar)
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 9) MultiLayerNet-Klasse
Ein Amateur versuchte Deep Learning mit Caffe (Einführung)
Ein Amateur versuchte Deep Learning mit Caffe (Übung)
Ein Amateur hat Deep Learning mit Caffe ausprobiert (Übersicht)
Python vs Ruby "Deep Learning von Grund auf neu" Zusammenfassung
"Deep Learning from Grund" Memo zum Selbststudium (10) MultiLayerNet-Klasse
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 3 Implementierung eines dreischichtigen neuronalen Netzwerks
[Python] [Verarbeitung natürlicher Sprache] Ich habe Deep Learning ausprobiert (auf Japanisch von Grund auf neu erstellt)
Deep Learning von Grund auf neu Die Theorie und Implementierung des mit Python erlernten Deep Learning Kapitel 3
Lua-Version Deep Learning von Grund auf neu Teil 5.5 [Bereitstellung von pkl-Dateien in Lua Torch]
[Für Anfänger] Was ist in Deep Learning von Grund auf neu geschrieben?
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 19) Datenerweiterung