Deep Learning von Grund auf neu Java Kapitel 4 Lernen neuronaler Netze

Inhaltsverzeichnis

4.2 Verlustfunktion

Funktionen, damit Sie verschiedene bisher angezeigte Funktionen problemlos aufrufen können ) Es ist in der Klasse zusammengefasst.

4.2.1 Summe der Fehlerquadrate

public static double mean_squared_error(INDArray y, INDArray t) {
    INDArray diff = y.sub(t);
    //Nimmt ein inneres Produkt mit Ihrer eigenen Translokationsmatrix.
    return 0.5 * diff.mmul(diff.transpose()).getDouble(0);
}

public static double mean_squared_error2(INDArray y, INDArray t) {
    //Verwenden Sie die Quadratabstandsfunktion von ND4J.
    return 0.5 * (double)y.squaredDistance(t);
}

INDArray t = Nd4j.create(new double[] {0, 0, 1, 0, 0, 0, 0, 0, 0, 0});
INDArray y = Nd4j.create(new double[] {0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0});
assertEquals(0.097500000000000031, mean_squared_error(y, t), 5e-6);
assertEquals(0.097500000000000031, mean_squared_error2(y, t), 5e-6);
// LossFunctions.LossFunction.Dies kann auch mit MSE erreicht werden.
assertEquals(0.097500000000000031, LossFunctions.score(t, LossFunctions.LossFunction.MSE, y, 0, 0, false), 5e-6);
y = Nd4j.create(new double[] {0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0});
assertEquals(0.59750000000000003, mean_squared_error(y, t), 5e-6);
assertEquals(0.59750000000000003, mean_squared_error2(y, t), 5e-6);
assertEquals(0.59750000000000003, LossFunctions.score(t, LossFunctions.LossFunction.MSE, y, 0, 0, false), 5e-6);

4.2.2 Kreuzentropiefehler

public static double cross_entropy_error(INDArray y, INDArray t) {
    double delta = 1e-7;
    // Python: return -np.sum(t * np.log(y + delta))
    return -t.mul(Transforms.log(y.add(delta))).sumNumber().doubleValue();
}

INDArray t = Nd4j.create(new double[] {0, 0, 1, 0, 0, 0, 0, 0, 0, 0});
INDArray y = Nd4j.create(new double[] {0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0});
assertEquals(0.51082545709933802, cross_entropy_error(y, t), 5e-6);
//Dies kann auch mit Verlustfunktionen erreicht werden.
assertEquals(0.51082545709933802, LossFunctions.score(t, LossFunctions.LossFunction.MCXENT, y, 0, 0, false), 5e-6);
y = Nd4j.create(new double[] {0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0});
assertEquals(2.3025840929945458, cross_entropy_error(y, t), 5e-6);

4.2.3 Mini-Batch-Lernen

Verwenden Sie die Klasse DataSet von ND4J, um Stichproben zufällig zu extrahieren.

//Laden Sie den MNIST-Datensatz.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
assertArrayEquals(new int[] {60000, 784}, train.normalizedImages().shape());
assertArrayEquals(new int[] {60000, 10}, train.oneHotLabels().shape());
//Extrahiere zufällig 10 Bilder.
//Speichern Sie das Bild und die Beschriftung einmal im DataSet und nehmen Sie die angegebene Anzahl von Proben als Probe heraus.
DataSet ds = new DataSet(train.normalizedImages(), train.oneHotLabels());
DataSet sample = ds.sample(10);
assertArrayEquals(new int[] {10, 784}, sample.getFeatureMatrix().shape());
assertArrayEquals(new int[] {10, 10}, sample.getLabels().shape());
//Um zu bestätigen, dass das Bild der erhaltenen Probe mit dem Etikettenwert übereinstimmt
//Exportieren Sie das Beispielbild als PNG-Datei.
// one-Konvertiert ein Hot-Format-Label in den ursprünglichen Label-Wert. (Finden Sie den Index des Maximalwerts jeder Zeile)
INDArray indexMax = Nd4j.getExecutioner().exec(new IAMax(sample.getLabels()), 1);
if (!Constants.SampleImagesOutput.exists())
    Constants.SampleImagesOutput.mkdirs();
for (int i = 0; i < 10; ++i) {
    //Der Dateiname lautet"(Ordnungsnummer)-(Beschriftungswert).png "Es wird sein.
    File f = new File(Constants.SampleImagesOutput,
        String.format("%05d-%d.png ",
            i, indexMax.getInt(i)));
    MNISTImages.writePngFile(sample.getFeatures().getRow(i), train.rows, train.columns, f);
}

4.2.4 [Batch-kompatible Version] Implementierung eines Cross-Entropy-Fehlers

public static double cross_entropy_error2(INDArray y, INDArray t) {
    int batch_size = y.size(0);
    return -t.mul(Transforms.log(y.add(1e-7))).sumNumber().doubleValue() / batch_size;
}

//Für einzelne Daten
INDArray t = Nd4j.create(new double[] {0, 0, 1, 0, 0, 0, 0, 0, 0, 0});
INDArray y = Nd4j.create(new double[] {0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0});
assertEquals(0.51082545709933802, cross_entropy_error2(y, t), 5e-6);
//Chargengröße=Bei 2 (2 identische Daten)
t = Nd4j.create(new double[][] {
    {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 1, 0, 0, 0, 0, 0, 0, 0}});
y = Nd4j.create(new double[][] {
    {0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0},
    {0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0}});
assertEquals(0.51082545709933802, cross_entropy_error2(y, t), 5e-6);
// todo: one-Implementierung eines Kreuzentropiefehlers, wenn dieser nicht als heiß dargestellt wird

4.3 Numerische Differenzierung

4.3.1 Differenzierung

public static double numerical_diff_bad(DoubleUnaryOperator f, double x) {
    double h = 10e-50;
    return (f.applyAsDouble(x + h) - f.applyAsDouble(x)) / h;
}

assertEquals(0.0, (float)1e-50, 1e-52);

4.3.2 Beispiel für eine numerische Differenzierung

public static double numerical_diff(DoubleUnaryOperator f, double x) {
    double h = 1e-4;
    return (f.applyAsDouble(x + h) - f.applyAsDouble(x - h)) / (h * 2);
}

public double function_1(double x) {
    return 0.01 * x * x + 0.1 * x;
}

public double function_1_diff(double x) {
    return 0.02 * x + 0.1;
}
assertEquals(0.200, numerical_diff(this::function_1, 5), 5e-6);
assertEquals(0.300, numerical_diff(this::function_1, 10), 5e-6);
assertEquals(0.200, function_1_diff(5), 5e-6);
assertEquals(0.300, function_1_diff(10), 5e-6);

4.3.3 Teilweise Differenzierung

public double function_2(INDArray x) {
    double x0 = x.getDouble(0);
    double x1 = x.getDouble(1);
    return x0 * x0 + x1 * x1;
}

DoubleUnaryOperator function_tmp1 = x0 -> x0 * x0 + 4.0 * 4.0;
assertEquals(6.00, numerical_diff(function_tmp1, 3.0), 5e-6);
DoubleUnaryOperator function_tmp2 = x1 -> 3.0 * 3.0 + x1 * x1;
assertEquals(8.00, numerical_diff(function_tmp2, 4.0), 5e-6);

4.4 Farbverlauf

public double function_2(INDArray x) {
    double x0 = x.getFloat(0);
    double x1 = x.getFloat(1);
    return x0 * x0 + x1 * x1;
    //Oder
    // return x.mul(x).sumNumber().doubleValue();
    //Alternativ können Sie das innere Produkt mit der transponierten Matrix wie folgt nehmen.
    // return x.mmul(x.transpose()).getDouble(0);
}

assertEquals("[6.00,8.00]", Util.string(Functions.numerical_gradient(this::function_2, Nd4j.create(new double[] {3.0, 4.0}))));
assertEquals("[0.00,4.00]", Util.string(Functions.numerical_gradient(this::function_2, Nd4j.create(new double[] {0.0, 2.0}))));
assertEquals("[6.00,0.00]", Util.string(Functions.numerical_gradient(this::function_2, Nd4j.create(new double[] {3.0, 0.0}))));

4.4.1 Gradientenmethode

public static INDArray gradient_descent(INDArrayFunction f, INDArray init_x, double lr, int step_num) {
    INDArray x = init_x;
    for (int i = 0; i < step_num; ++i) {
        INDArray grad = Functions.numerical_gradient(f, x);
        INDArray y = x.sub(grad.mul(lr));
//            System.out.printf("step:%d x=%s grad=%s x'=%s%n", i, x, grad, y);
        x = y;
    }
    return x;
}

// lr = 0.1
INDArray init_x = Nd4j.create(new double[] {-3.0, 4.0});
INDArray r = gradient_descent(this::function_2, init_x, 0.1, 100);
assertEquals("[-0.00,0.00]", Util.string(r));
assertEquals(-6.11110793e-10, r.getDouble(0), 5e-6);
assertEquals(8.14814391e-10, r.getDouble(1), 5e-6);
//Beispiel für eine zu große Lernrate: lr = 10.0
r = gradient_descent(this::function_2, init_x, 10.0, 100);
//Es ist nicht dasselbe wie das Python-Ergebnis, aber auf jeden Fall erhalten Sie nicht das richtige Ergebnis.
assertEquals("[-763,389.44,1,017,852.62]", Util.string(r));
//Beispiel, bei dem die Lernrate zu gering ist: lr = 1e-10
r = gradient_descent(this::function_2, init_x, 1e-10, 100);
assertEquals("[-3.00,4.00]", Util.string(r));

4.4.2 Gradient in Bezug auf das neuronale Netzwerk

static class simpleNet {

    /**Gewicht*/
    public final INDArray W;

    /**
     *Gewicht 0.0 bis 1.Initialisieren Sie mit einer Zufallszahl im Bereich von 0.
     */
    public simpleNet() {
        try (Random r = new DefaultRandom()) {
            //Erstellen Sie eine Matrix von Zufallszahlen basierend auf einer 2x3-Gauß-Verteilung.
            W = r.nextGaussian(new int[] {2, 3});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *Gewichte, um sicherzustellen, dass die Ergebnisse mit diesem Buch übereinstimmen
     *Ermöglicht die Abgabe von außen.
     */
    public simpleNet(INDArray W) {
        this.W = W.dup();   //Defensiv kopieren.
    }

    public INDArray predict(INDArray x) {
        return x.mmul(W);
    }

    public double loss(INDArray x, INDArray t) {
        INDArray z = predict(x);
        INDArray y = Functions.softmax(z);
        double loss = Functions.cross_entropy_error(y, t);
        return loss;
    }
}

//Geben Sie für das Gewicht den gleichen Wert wie für dieses Buch anstelle einer Zufallszahl an.
INDArray W = Nd4j.create(new double[][] {
    {0.47355232, 0.9977393, 0.84668094},
    {0.85557411, 0.03563661, 0.69422093},
});
simpleNet net = new simpleNet(W);
assertEquals("[[0.47,1.00,0.85],[0.86,0.04,0.69]]", Util.string(net.W));
INDArray x = Nd4j.create(new double[] {0.6, 0.9});
INDArray p = net.predict(x);
assertEquals("[1.05,0.63,1.13]", Util.string(p));
assertEquals(2, Functions.argmax(p).getInt(0));
INDArray t = Nd4j.create(new double[] {0, 0, 1});
assertEquals(0.92806853663411326, net.loss(x, t), 5e-6);
//Die Funktionsdefinition verwendet einen Lambda-Ausdruck.
INDArrayFunction f = dummy -> net.loss(x, t);
INDArray dW = Functions.numerical_gradient(f, net.W);
assertEquals("[[0.22,0.14,-0.36],[0.33,0.22,-0.54]]", Util.string(dW));

4.5 Implementierung des Lernalgorithmus

4.5.1 Zweischichtige neuronale Netzwerkklasse

Die Klasse des zweischichtigen neuronalen Netzwerks ist TwoLayerNet. Gewichte und Verzerrungen werden in TwoLayerParams anstelle von Map gespeichert .. Die Zufallszahl verwendet die ND4J-Schnittstelle Randam i. In meiner Umgebung dauert es ungefähr 5 bis 10 Minuten.

TwoLayerNet net = new TwoLayerNet(784, 100, 10);
assertArrayEquals(new int[] {784, 100}, net.parms.get("W1").shape());
assertArrayEquals(new int[] {1, 100}, net.parms.get("b1").shape());
assertArrayEquals(new int[] {100, 10}, net.parms.get("W2").shape());
assertArrayEquals(new int[] {1, 10}, net.parms.get("b2").shape());
try (Random r = new DefaultRandom()) {
    INDArray x = r.nextGaussian(new int[] {100, 784});
    INDArray t = r.nextGaussian(new int[] {100, 10});
    INDArray y = net.predict(x);
    assertArrayEquals(new int[] {100, 10}, y.shape());
    Params grads = net.numerical_gradient(x, t);
    assertArrayEquals(new int[] {784, 100}, grads.get("W1").shape());
    assertArrayEquals(new int[] {1, 100}, grads.get("b1").shape());
    assertArrayEquals(new int[] {100, 10}, grads.get("W2").shape());
    assertArrayEquals(new int[] {1, 10}, grads.get("b2").shape());
}

4.5.2 Implementierung des Mini-Batch-Lernens

Die Verwendung von MNIST-Daten ist sehr zeitaufwändig. In meiner Umgebung dauert es ungefähr 90 Sekunden für jede Schleife. Wenn Sie also 10.000 Mal eine Schleife durchführen, dauert es ungefähr 10 Tage.

//Laden Sie den MNIST-Datensatz.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
INDArray x_train = train.normalizedImages();
INDArray t_train = train.oneHotLabels();
assertArrayEquals(new int[] {60000, 784}, x_train.shape());
assertArrayEquals(new int[] {60000, 10}, t_train.shape());
List<Double> train_loss_list =  new ArrayList<>();
int iters_num = 10000;
// int train_size = images.size(0);
 int batch_size = 100;
double learning_rate = 0.1;
TwoLayerNet network = new TwoLayerNet(784, 50, 10);
// batch_Daten für die Größe werden zufällig abgerufen.
for (int i = 0; i < iters_num; ++i) {
    long start = System.currentTimeMillis();
    //Holen Sie sich eine Mini-Charge
    DataSet ds = new DataSet(x_train, t_train);
    DataSet sample = ds.sample(batch_size);
    INDArray x_batch = sample.getFeatureMatrix();
    INDArray t_batch = sample.getLabels();
    Params grad =  network.numerical_gradient(x_batch, t_batch);
    network.parms.update((p, a) -> p.subi(a.mul(learning_rate)), grad);
    //Aufzeichnung des Lernfortschritts
    double loss = network.loss(x_batch, t_batch);
    train_loss_list.add(loss);
    System.out.printf("iteration %d loss=%f elapse=%dms%n",
        i, loss, System.currentTimeMillis() - start);
}

4.5.3 Auswertung mit Testdaten

Die Verwendung von MNIST-Daten ist sehr zeitaufwändig. In meiner Umgebung dauert es ungefähr 90 Sekunden für jede Schleife. Wenn Sie also 10.000 Mal eine Schleife durchführen, dauert es ungefähr 10 Tage. Daher habe ich es nicht bis zum Ende ausgeführt, aber es dauerte 4,6 Stunden, bis die Erkennungsgenauigkeit sowohl der Trainingsdaten als auch der Testdaten 90% oder mehr betrug und die Anzahl der Schleifen 214 betrug. Wie Sie der Grafik im Buch entnehmen können, steigt sie relativ schnell auf etwa 80%. Daher ist es möglicherweise besser, anzuhalten, wenn die Erkennungsgenauigkeit den Schwellenwert überschreitet, anstatt 10.000-mal zu schleifen.

//Laden Sie den MNIST-Datensatz.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
INDArray x_train = train.normalizedImages();
INDArray t_train = train.oneHotLabels();
MNISTImages test = new MNISTImages(Constants.TestImages, Constants.TestLabels);
INDArray x_test = test.normalizedImages();
INDArray t_test = test.oneHotLabels();
assertArrayEquals(new int[] {60000, 784}, x_train.shape());
assertArrayEquals(new int[] {60000, 10}, t_train.shape());
List<Double> train_loss_list =  new ArrayList<>();
List<Double> train_acc_list = new ArrayList<>();
List<Double> test_acc_list = new ArrayList<>();
int iters_num = 10000;
int train_size = x_train.size(0);
int batch_size = 100;
double learning_rate = 0.01;
int iter_per_epoch = Math.max(train_size / batch_size, 1);
TwoLayerNet network = new TwoLayerNet(784, 50, 10);
// batch_Daten für die Größe werden zufällig abgerufen.
for (int i = 0; i < iters_num; ++i) {
    long start = System.currentTimeMillis();
    //Holen Sie sich eine Mini-Charge
    DataSet ds = new DataSet(x_train, t_train);
    DataSet sample = ds.sample(batch_size);
    INDArray x_batch = sample.getFeatureMatrix();
    INDArray t_batch = sample.getLabels();
    Params grad =  network.numerical_gradient(x_batch, t_batch);
    network.parms.update((p, a) -> p.subi(a.mul(learning_rate)), grad);
    //Aufzeichnung des Lernfortschritts
    double loss = network.loss(x_batch, t_batch);
    train_loss_list.add(loss);
    //Berechnung des Erkennungssystems für jede Epoche
    if (i % iter_per_epoch == 0) {
        double train_acc = network.accuracy(x_train, t_train);
        double test_acc = network.accuracy(x_test, t_test);
        train_acc_list.add(train_acc);
        test_acc_list.add(test_acc);
        System.out.printf("train acc, test acc | %s, %s%n",
            train_acc, test_acc);
    }
    System.out.printf("iteration %d loss=%f elapse=%dms%n",
        i, loss, System.currentTimeMillis() - start);
}

Recommended Posts

Deep Learning von Grund auf neu Java Kapitel 4 Lernen neuronaler Netze
[Deep Learning von Grund auf neu] in Java 3. Neuronales Netzwerk
Deep Learning Java von Grund auf neu Kapitel 3 Neuronales Netzwerk
Deep Learning Java von Grund auf neu Kapitel 1 Einführung
Deep Learning Java von Grund auf neu Kapitel 2 Perceptron
Deep Learning Java von Grund auf 6.4 Regularisierung
Deep Learning Java von Grund auf neu Kapitel 5 Methode zur Fehlerrückübertragung
Lernen Sie Deep Learning von Grund auf in Java.
Deep Learning Java von Grund auf 6.1 Parameteraktualisierung
Deep Learning Java von Grund auf 6.3 Batch-Normalisierung
Deep Learning Java von Grund auf 6.2 Anfangswert des Gewichts
Schnellstes PC-Setup für tiefes Lernen von Grund auf
[Deep Learning von Grund auf neu] 2. In Java gibt es kein NumPy.
[Deep Learning von Grund auf neu] in Java 1. Zur Zeit Differenzierung und teilweise Differenzierung
Java-Leben von vorne anfangen
Backpropagation Neural Network Java Code zum Lernen des XOR-Gate-Musters
[DL4J] Erstes Java Deep Learning (Handschriftliche Zeichenerkennung über ein vollständig verbundenes neuronales Netzwerk)
Java lernen (0)
Java Scratch Scratch
Erste Schritte für tiefes Lernen in Java
Effektives Java Kapitel 2
Java-Lerntag 5
Effektives Java Kapitel 6 34-35
Effektives Java Kapitel 4 15-22
Effektives Java Kapitel 3
Java-Lerntag 2
Java-Lerntag 1
Erstellen Sie die VS Code + WSL + Java + Gradle-Umgebung von Grund auf neu
[Hinweis] Erstellen Sie mit Docker eine Java-Umgebung von Grund auf neu
Schnell lernen Java "Einführung?" Teil 3 Von der Programmierung wegreden