[PYTHON] "Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN

Während ich "Deep Learning from Grund" (gelesen von Yasuki Saito, veröffentlicht von O'Reilly Japan) lese, werde ich die Websites notieren, auf die ich mich bezog. Teil 10

Wenn es um Faltungsnetzwerke in Kapitel 7 geht, sieht es ganz anders aus als in Kapitel 6. Es sieht so aus, als würden Sie viele verschiedene Dinge tun, aber am Ende werden Sie die Gewichts- und Vorspannungsverläufe finden und speichern. Mit anderen Worten, das Grundprinzip hat sich überhaupt nicht geändert, es sind die Eingabedaten, die sich geändert haben

P207 Wenn die Eingabedaten ein Bild sind, hat das Bild normalerweise eine dreidimensionale Form in vertikaler, horizontaler und Kanalrichtung. Beim Eintritt in die vollständig verbundene Schicht müssen die dreidimensionalen Daten jedoch flache eindimensionale Daten sein. In früheren Beispielen unter Verwendung des MNIST-Datensatzes hatte das Eingabebild die Form (1, 28, 28) - 1 Kanal, 28 Pixel hoch, 28 Pixel breit - aber in einer Reihe angeordnet. Sie haben 784 Daten in die erste affine Ebene eingegeben. ・ ・ ・ Die Faltungsschicht hingegen behält ihre Form. Im Fall eines Bildes werden die Eingabedaten als 3D-Daten empfangen und die Daten werden auch als 3D-Daten an die nächste Ebene ausgegeben. Daher kann CNN Daten mit Formen wie Bildern (möglicherweise) korrekt verstehen.

Tatsächlich habe ich selbst dieses Selbststudien-Memo Nr. 6-2 verwendet, um Kaggles Katzen- und Hundedatensätze um 1 zu verarbeiten Ich konvertiere es in eine Dimension und benutze es. Wenn dies in drei Dimensionen verarbeitet werden kann, kann sich die Erkennungsrate verbessern.

Faltschicht, Polsterung, Schritt

Diese Erklärungen sind überhaupt nicht schwierig, und ich kann sie als solche verstehen, aber da diese Formel plötzlich auf P212 erscheint, was ist das? Ist das wirklich der Fall? Also habe ich darüber nachgedacht. OH = \frac{H + 2P - FH}{S} + 1 OW = \frac{W + 2P - FW}{S} + 1

Denken wir vorerst darüber nach, dass es kein S (Schritt) gibt. Lassen Sie uns einige Eingabegrößen und Filtergrößen überprüfen p7-1.jpg p7-2.jpg

Wenn die Eingangsgröße (n, n) und die Filtergröße (m, m) Die Ausgabegröße scheint zu sein (n-m + 1, n-m + 1). Wenn Sie den Filter auf die obere linke Ecke anwenden, können Sie ihn nach rechts (nm) drehen. Es kann sich nach unten drehen (nm). Wenn Sie also 1 Minute in der oberen linken Ecke hinzufügen, ist es nm + 1?

Was passiert also mit Schritten? Wenn der Schritt 2 ist, wird die Anzahl der Umdrehungen nach rechts (nm) halbiert. (Nm) / 2 Wenn es 3 ist, wird es 1/3.

Mit anderen Worten, die Häufigkeit, mit der Sie sich bewegen können, beträgt (nm) / s Die Ausgabegröße beträgt (nm) / s + 1.

Angenommen, die Eingangsdatengröße ist (H, W), die Auffüllung ist P und die Filtergröße ist (FH, FW) n = H + 2 × P In ähnlicher Weise ist n = W + 2 × P. m=FH        n=FW Damit Die Ausgabegröße ist  OH=(H+2×P-FH)/s + 1  OW=(W+2×P-FW)/s + 1

Schulung und Prüfung von MNIST-Daten

Ab P230 gibt es eine Beschreibung der Klasse SimpleConvNet als Beispiel für das Training von MNIST-Daten. Lassen Sie mich mit dieser Klasse lernen

import sys, os
sys.path.append(os.pardir)  #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import numpy as np

from dataset.mnist import load_mnist
from common.simple_convnet import SimpleConvNet
from common.trainer import Trainer

#Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

max_epochs = 20
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)
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=max_epochs, mini_batch_size=100,
                  optimizer='Adam', optimizer_param={'lr': 0.001},
                  evaluate_sample_num_per_epoch=1000, verbose=False)
trainer.train()

Ich habe versucht, den Urteilsinhalt der Testdaten zu überprüfen.

import numpy as np
from common.simple_convnet import SimpleConvNet
from dataset.mnist import load_mnist
import pickle

import matplotlib.pyplot as plt

def showImg(x):
    example = x.reshape((28, 28))
    plt.figure()
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(example, cmap=plt.cm.binary)
    plt.show()
    return

#Mit Testdaten auswerten
x = x_test
t = t_test

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)
network.load_params("params.pkl")
    
y = network.predict(x)

accuracy_cnt = 0
for i in range(len(x)):
    p= np.argmax(y[i])
    #print(str(x[i]) + " : " + str(p))
    if p == t[i]:
        accuracy_cnt += 1
    else:
        print("Richtige Antwort:"+str(t[i])+"Inferenzergebnis:"+str(p))
        showImg(x[i])

print("Accuracy:" + str(float(accuracy_cnt) / len(x))) 

Infolgedessen ist die richtige Antwortrate

Accuracy:0.988

Was falsch war, ist so p6-.jpg

p6-2.jpg

p6-3.jpg

p6-4.jpg

Die Verarbeitung von 60.000 Daten dauerte jedoch Stunden. Als ich nach dem Lernen versuchte, die Testdaten zu verarbeiten, war ich außerdem von dem Problem des unzureichenden Speichers betroffen und konnte nicht einfach fortfahren. Ist Deep Learning in Memory 4G nicht zumutbar?

Vorerst konnte ich bestätigen, dass ich mit CNN mit hoher Genauigkeit lernen konnte.

damit

Wie immer möchte ich den Inhalt des Programms verfolgen.

SimpleConvNet-Klasse

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import pickle
import numpy as np
from collections import OrderedDict
from common.layers import *
from common.gradient import numerical_gradient


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):
        filter_num = conv_param['filter_num']
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))

        #Gewichtsinitialisierung
        self.params = {}
        self.params['W1'] = weight_init_std * \
                            np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * \
                            np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * \
                            np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        #Schichterzeugung
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
                                           conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = Relu()
        self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])

        self.last_layer = SoftmaxWithLoss()

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)

        return x

    def loss(self, x, t):
        y = self.predict(x)
        return self.last_layer.forward(y, t)

    def accuracy(self, x, t, batch_size=100):
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        acc = 0.0
        
        for i in range(int(x.shape[0] / batch_size)):
            tx = x[i*batch_size:(i+1)*batch_size]
            tt = t[i*batch_size:(i+1)*batch_size]
            y = self.predict(tx)
            y = np.argmax(y, axis=1)
            acc += np.sum(y == tt) 
        
        return acc / x.shape[0]

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.last_layer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        #Aufbau
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
        grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads
        
    def save_params(self, file_name="params.pkl"):
        params = {}
        for key, val in self.params.items():
            params[key] = val
        with open(file_name, 'wb') as f:
            pickle.dump(params, f)

    def load_params(self, file_name="params.pkl"):
        with open(file_name, 'rb') as f:
            params = pickle.load(f)
        for key, val in params.items():
            self.params[key] = val

        for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
            self.layers[key].W = self.params['W' + str(i+1)]
            self.layers[key].b = self.params['b' + str(i+1)]

Der einzige Unterschied besteht darin, dass die Ebenen gestapelt sind und die anderen sich nicht wesentlich von der MultiLayerNet-Klasse unterscheiden.

        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
                                           conv_param['stride'], conv_param['pad'])

Die Convolution-Klasse ist auch in layer.py definiert

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad
        
        #Zwischendaten (werden rückwärts verwendet)
        self.x = None   
        self.col = None
        self.col_W = None
        
        #Gradient des Gewichts / Bias-Parameters
        self.dW = None
        self.db = None

    def forward(self, x):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2*self.pad - FW) / self.stride)

        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0,2,3,1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

im2col Das Herzstück ist die im2col-Funktion. In util.py definiert

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_data.shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col

Dies scheint also die Ursache dafür zu sein, dass der Speicher knapp wird. Wenn die Anzahl der zu verarbeitenden Datenzeilen zunimmt, tritt hier ein Speicherfehler auf.

In den ersten drei Zeilen überprüfen wir die Größe der Eingabedaten und berechnen die Ausgabegröße aus der Eingabegröße und der Filtergröße. Der Grund, warum // für die Schrittteilung verwendet wird, scheint darin zu liegen, nach dem Dezimalpunkt abzuschneiden, wenn es nicht teilbar ist.

Bestätigung der Größe der Testdaten dieser Eingangsdaten MNIST

len(x_test)  #Die Anzahl der Daten

10000

len(x_test[0]) #Kanal

1

len(x_test[0][0]) #Höhe

28

len(x_test[0][0][0]) #Breite

28

Überprüfen Sie die Größe des Filters W1

len(network.params['W1']) #Anzahl der Filter

30

len(network.params['W1'][0]) #Anzahl der Kanäle

1

len(network.params['W1'][0][0]) #Filterhöhe

5

len(network.params['W1'][0][0][0]) #Filterbreite

5

Bestätigung der Polsterung und des Schrittes

                        conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},

Das Auffüllen von 0 und Schritt 1 wird beim Erstellen des Netzwerkobjekts angegeben.

Ausgabegröße der Faltungsschicht

OH = \frac{H + 2P - FH}{S} + 1 = (28 + 0 - 5)/1 +1 = 24 OW = \frac{W + 2P - FW}{S} + 1 = (28 + 0 - 5)/1 +1 = 24 Sollte sein.

len(network.layers['Conv1'].forward(x_test))  #Die Anzahl der Daten

10000

len(network.layers['Conv1'].forward(x_test)[0]) #Anzahl der Filter

30

len(network.layers['Conv1'].forward(x_test)[0][0]) #Ausgangshöhe

24

len(network.layers['Conv1'].forward(x_test)[0][0][0]) #Ausgabebreite

24

Faltungsschichtverfolgung

        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
                                           conv_param['stride'], conv_param['pad'])
class Convolution:
(Weggelassen)
    def forward(self, x):
        FN, C, FH, FW = self.W.shape   # 30, 1, 5, 5
        N, C, H, W = x.shape           # 10000, 1, 28, 28
        out_h = 1 + int((H + 2*self.pad - FH) / self.stride) # 24
        out_w = 1 + int((W + 2*self.pad - FW) / self.stride) # 24

        col = im2col(x, FH, FW, self.stride, self.pad)
(Weggelassen)
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_data.shape          # 10000, 1, 28, 28
    out_h = (H + 2*pad - filter_h)//stride + 1 # 24
    out_w = (W + 2*pad - filter_w)//stride + 1 # 24

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')

input_data ist 4 Dimensionen (10000 Datenzeilen, 1 Kanal, 28 Höhe, 28 Breite) Wenn pad = 0 ist, wird [(0,0), (0,0), (0, 0), (0, 0)] nicht aufgefüllt. Wenn Pad = 1, [(0,0), (0,0), (1, 1), (1, 1)] Pad nacheinander oben, unten, links und rechts von Höhe und Breite. Wenn pad = 2, [(0,0), (0,0), (2, 2), (2, 2)] Pads jeweils zwei oben, unten, links und rechts von Höhe und Breite. In diesem Programmbeispiel ist pad = 0. Das gleiche wie input_data wird in img gesetzt.

    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) #10000, 1, 5, 5, 24, 24

Die Eingabedaten (Bild Bild) werden in die Array-Spalte erweitert. Erstellen Sie jedoch als Container zum Erweitern der Daten ein Array mit der Größe (Anzahl der Daten, Kanal, Filterhöhe, Filterbreite, Ausgabehöhe, Ausgabebreite). ..

    for y in range(filter_h):        
        y_max = y + stride*out_h     
        for x in range(filter_w):    
            x_max = x + stride*out_w 
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col

Ich kann das Bild hier überhaupt nicht bekommen, deshalb habe ich es mit der folgenden vereinfachten Sequenz getestet.

import numpy as np
N=1
C=1
H=8
W=8
filter_h=4
filter_w=4
stride=2
out_h=3
out_w=3
img= np.arange(64).reshape(N, C, 8, 8)
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

for y in range(filter_h):        
    y_max = y + stride*out_h     
    for x in range(filter_w):    
        x_max = x + stride*out_w 
        col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)

col

array([[ 0., 1., 2., 3., 8., 9., 10., 11., 16., 17., 18., 19., 24., 25., 26., 27.], [ 2., 3., 4., 5., 10., 11., 12., 13., 18., 19., 20., 21., 26., 27., 28., 29.], [ 4., 5., 6., 7., 12., 13., 14., 15., 20., 21., 22., 23., 28., 29., 30., 31.], [16., 17., 18., 19., 24., 25., 26., 27., 32., 33., 34., 35., 40., 41., 42., 43.], [18., 19., 20., 21., 26., 27., 28., 29., 34., 35., 36., 37., 42., 43., 44., 45.], [20., 21., 22., 23., 28., 29., 30., 31., 36., 37., 38., 39., 44., 45., 46., 47.], [32., 33., 34., 35., 40., 41., 42., 43., 48., 49., 50., 51., 56., 57., 58., 59.], [34., 35., 36., 37., 42., 43., 44., 45., 50., 51., 52., 53., 58., 59., 60., 61.], [36., 37., 38., 39., 44., 45., 46., 47., 52., 53., 54., 55., 60., 61., 62., 63.]])

In Spalte [0] wird der Teil, auf den der Filter zuerst angewendet wird, aus den Eingabedaten extrahiert. col [1] ist der Teil, in dem der Filter angewendet wird, indem der Schritt 2 um zwei nach rechts verschoben wird. Unten wird der Teil extrahiert und angeordnet, auf den der Filter 9 Mal angewendet wird.

p7-3.jpg p7-4.jpg

Ich bin nicht sicher, was ich tue, aber ich kann das Ergebnis verstehen.

Wenn Sie den 4x4-Filter in eine Spalte umformen und Spalten- und Punktoperationen ausführen, können Sie das Ergebnis der neunmaligen Anwendung des Filters in einer Operation erhalten.

#Convolution.forward

        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

Referenz

Vollständiges Verständnis der Funktion numpy.pad Manipulieren Sie das zweidimensionale Array frei. [Initialisierung / Referenz / Extraktion / Berechnung / Verlagerung]

Teil 10

Recommended Posts

"Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 19) Datenerweiterung
"Deep Learning from Grund" Memo zum Selbststudium (Teil 12) Deep Learning
Selbststudien-Memo "Deep Learning from Grund" (Nr. 18) Eins! Miau! Grad-CAM!
Selbststudien-Memo "Deep Learning from Grund" (Nr. 15) TensorFlow-Anfänger-Tutorial
Selbststudien-Memo "Deep Learning from Grund" (unlesbares Glossar)
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 9) MultiLayerNet-Klasse
"Deep Learning from Grund" Memo zum Selbststudium (10) MultiLayerNet-Klasse
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 10-2) Anfangswert des Gewichts
Deep Learning von Grund auf neu
[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]
[Lernnotiz] Deep Learning von Grund auf neu gemacht [~ Kapitel 4]
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 16) Ich habe versucht, SimpleConvNet mit Keras zu erstellen
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 17) Ich habe versucht, DeepConvNet mit Keras zu erstellen
Deep Learning von Grund auf neu Kapitel 2 Perceptron (Memo lesen)
[Lernnotiz] Deep Learning von Grund auf ~ Implementierung von Dropout ~
Deep Learning von Grund auf 1-3 Kapitel
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 14) Führen Sie das Programm in Kapitel 4 in Google Colaboratory aus
Deep Learning / Deep Learning von Grund auf neu 2 Kapitel 4 Memo
Deep Learning / Deep Learning von Grund auf neu Kapitel 3 Memo
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
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
"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
Warum ModuleNotFoundError: In "Deep Learning from Grund" wird kein Modul mit dem Namen "didaset.mnist" angezeigt.
"Deep Learning from Grund" Memo zum Selbststudium (Teil 8) Ich habe die Grafik in Kapitel 6 mit matplotlib gezeichnet
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
GitHub des guten Buches "Deep Learning von Grund auf neu"
Django Memo # 1 von Grund auf neu
Python vs Ruby "Deep Learning von Grund auf neu" Zusammenfassung
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
Anwendung von Deep Learning 2 von Grund auf neu Spam-Filter
<Kurs> Tiefes Lernen: Day2 CNN
Deep Learning / LSTM Scratch Code
[Deep Learning von Grund auf neu] Implementierung der Momentum-Methode und der AdaGrad-Methode
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
Erstellen Sie mit Docker eine Umgebung für "Deep Learning von Grund auf neu"
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
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
Ich habe versucht, Perceptron Teil 1 [Deep Learning von Grund auf neu] zu implementieren.