Deep Learning Java von Grund auf neu Kapitel 3 Neuronales Netzwerk

Inhaltsverzeichnis

3.2 Aktivierungsfunktion

3.2.3 Implementierung der Schrittfunktion

Die Sprungfunktion kann wie folgt implementiert werden. Hier habe ich versucht, INDArray mit einer einfachen Funktion (DoubleFunction) abzubilden.

public static double step_function(double x) {
    if (x > 0)
        return 1.0;
    else
        return 0.0;
}

public static <T extends Number> INDArray map(INDArray x, DoubleFunction<T> func) {
    int size = x.length();
    INDArray result = Nd4j.create(size);
    for (int i = 0; i < size; ++i)
        result.put(0, i, func.apply(x.getDouble(i)));
    return result;
}

public static INDArray step_function(INDArray x) {
    return map(x, d -> d > 0.0 ? 1 : 0);
}

INDArray x = Nd4j.create(new double[] {-1.0, 1.0, 2.0});
assertEquals("[-1.00,1.00,2.00]", Util.string(x));
assertEquals("[0.00,1.00,1.00]", Util.string(step_function(x)));

3.2.4 Implementierung der Sigmoidfunktion

Die Sigmoidfunktion kann wie folgt implementiert werden. ND4J bietet eine Sigmoid-Funktion in der Klasse Transforms, sodass Sie sie auch verwenden können. ..

public static double sigmoid(double x) {
    return (double)(1.0 / (1.0 + Math.exp(-x)));
}

public static INDArray sigmoid(INDArray x) {
    //Operatoren können in Java nicht überladen werden
    //Beschrieben durch Aufrufen einer Methode.
    return Transforms.exp(x.neg()).add(1.0).rdiv(1.0);
    //Alternativ kann es unter Verwendung der oben beschriebenen Karte wie folgt implementiert werden.
    // return map(x, d -> sigmoid(d));
}


INDArray x = Nd4j.create(new double[] {-1.0, 1.0, 2.0});
assertEquals("[0.27,0.73,0.88]", Util.string(sigmoid(x)));
assertEquals("[0.27,0.73,0.88]", Util.string(Transforms.sigmoid(x)));
INDArray t = Nd4j.create(new double[] {1.0, 2.0, 3.0});
// A.rdiv(k)Ist k geteilt durch jedes Element von A.
assertEquals("[1.00,0.50,0.33]", Util.string(t.rdiv(1.0)));
}

3.2.7 ReLU-Funktion

Die ReLU-Funktion kann wie folgt implementiert werden. ND4J bietet eine Relu-Funktion in der Klasse Transforms, sodass Sie sie auch verwenden können.

public static INDArray relu(INDArray x) {
    return map(x, d -> Math.max(0.0, d));
}

INDArray x = Nd4j.create(new double[] {-4, -2, 0, 2, 4});
assertEquals("[0.00,0.00,0.00,2.00,4.00]", Util.string(relu(x)));
assertEquals("[0.00,0.00,0.00,2.00,4.00]", Util.string(Transforms.relu(x)));

3.3 Berechnung eines mehrdimensionalen Arrays

3.3.1 Mehrdimensionales Array

//Eindimensionales Array
INDArray A = Nd4j.create(new double[] {1, 2, 3, 4});
assertEquals("[1.00,2.00,3.00,4.00]", Util.string(A));
//In ND4J ist das eindimensionale Array ein zweidimensionales 1xN-Array.
assertArrayEquals(new int[] {1, 4}, A.shape());
//In ND4J ist die Anzahl der Dimensionen Rang()Durch die Methode erhalten.
assertEquals(2, A.rank());
assertEquals(1, A.size(0));  //Anzahl der Zeilen
assertEquals(4, A.size(1));  //Anzahl der Spalten
//Zweidimensionales Array
INDArray B = Nd4j.create(new double[][] {{1, 2}, {3, 4}, {5, 6}});
assertEquals("[[1.00,2.00],[3.00,4.00],[5.00,6.00]]", Util.string(B));
assertEquals(2, B.rank());
assertArrayEquals(new int[] {3, 2}, B.shape());

3.3.2 Matrixprodukt

In ND4J wird das innere Produkt von [INDArray] berechnet (https://nd4j.org/doc/org/nd4j/linalg/api/ndarray/INDArray.html).mmul (INDArray).

INDArray A = Nd4j.create(new double[][] {{1, 2}, {3, 4}});
assertArrayEquals(new int[] {2, 2}, A.shape());
INDArray B = Nd4j.create(new double[][] {{5, 6}, {7, 8}});
assertArrayEquals(new int[] {2, 2}, B.shape());
assertEquals("[[19.00,22.00],[43.00,50.00]]", Util.string(A.mmul(B)));

A = Nd4j.create(new double[][] {{1, 2, 3}, {4, 5, 6}});
assertArrayEquals(new int[] {2, 3}, A.shape());
B = Nd4j.create(new double[][] {{1, 2}, {3, 4}, {5, 6}});
assertArrayEquals(new int[] {3, 2}, B.shape());
assertEquals("[[22.00,28.00],[49.00,64.00]]", Util.string(A.mmul(B)));

INDArray C = Nd4j.create(new double[][] {{1, 2}, {3, 4}});
assertArrayEquals(new int[] {2, 2}, C.shape());
assertArrayEquals(new int[] {2, 3}, A.shape());
try {
    //Wenn in ND4J ein Fehler in der Anzahl der Elemente in der Matrix vorliegt, die das innere Produkt aufnehmen,
    //Löst eine ND4JIllegalStateException aus.
    A.mmul(C);
    fail();
} catch (ND4JIllegalStateException e) {
    assertEquals(
        "Cannot execute matrix multiplication: [2, 3]x[2, 2]: "
        + "Column of left array 3 != rows of right 2"
        , e.getMessage());
}

A = Nd4j.create(new double[][] {{1, 2}, {3, 4}, {5, 6}});
assertArrayEquals(new int[] {3, 2}, A.shape());
B = Nd4j.create(new double[] {7, 8});
assertArrayEquals(new int[] {1, 2}, B.shape());
//In ND4J ist ein eindimensionales Array eine 1 × N-Zeilenmatrix.
//Transponieren, um das Produkt zu finden()Muss in der Methode transponiert werden.
assertArrayEquals(new int[] {2, 1}, B.transpose().shape());
assertEquals("[23.00,53.00,83.00]", Util.string(A.mmul(B.transpose())));

3.3.3 Das Produkt der Matrix des neuronalen Netzwerks

INDArray X = Nd4j.create(new double[] {1, 2});
assertArrayEquals(new int[] {1, 2}, X.shape());
INDArray W = Nd4j.create(new double[][] {{1, 3, 5}, {2, 4, 6}});
assertEquals("[[1.00,3.00,5.00],[2.00,4.00,6.00]]", Util.string(W));
assertArrayEquals(new int[] {2, 3}, W.shape());
INDArray Y = X.mmul(W);
assertEquals("[5.00,11.00,17.00]", Util.string(Y));

3.4 Implementierung eines 3-Schicht-Neuronalen Netzwerks

3.4.2 Implementierung der Signalübertragung in jeder Schicht

INDArray X = Nd4j.create(new double[] {1.0, 0.5});
INDArray W1 = Nd4j.create(new double[][] {{0.1, 0.3, 0.5}, {0.2, 0.4, 0.6}});
INDArray B1 = Nd4j.create(new double[] {0.1, 0.2, 0.3});
assertArrayEquals(new int[] {2, 3}, W1.shape());
assertArrayEquals(new int[] {1, 2}, X.shape());
assertArrayEquals(new int[] {1, 3}, B1.shape());
INDArray A1 = X.mmul(W1).add(B1);
INDArray Z1 = Transforms.sigmoid(A1);
assertEquals("[0.30,0.70,1.10]", Util.string(A1));
assertEquals("[0.57,0.67,0.75]", Util.string(Z1));

INDArray W2 = Nd4j.create(new double[][] {{0.1, 0.4}, {0.2, 0.5}, {0.3, 0.6}});
INDArray B2 = Nd4j.create(new double[] {0.1, 0.2});
assertArrayEquals(new int[] {1, 3}, Z1.shape());
assertArrayEquals(new int[] {3, 2}, W2.shape());
assertArrayEquals(new int[] {1, 2}, B2.shape());
INDArray A2 = Z1.mmul(W2).add(B2);
INDArray Z2 = Transforms.sigmoid(A2);
assertEquals("[0.52,1.21]", Util.string(A2));
assertEquals("[0.63,0.77]", Util.string(Z2));

INDArray W3 = Nd4j.create(new double[][] {{0.1, 0.3}, {0.2, 0.4}});
INDArray B3 = Nd4j.create(new double[] {0.1, 0.2});
INDArray A3 = Z2.mmul(W3).add(B3);
//ND4J hat Identität in der Transforms-Klasse(INDArray)Eine Methode wird bereitgestellt.
INDArray Y = Transforms.identity(A3);
assertEquals("[0.32,0.70]", Util.string(A3));
assertEquals("[0.32,0.70]", Util.string(Y));
// Y.equals(A3)
assertEquals(A3, Y);

public static Map<String, INDArray> init_network() {
    Map<String, INDArray> network = new HashMap<>();
    network.put("W1", Nd4j.create(new double[][] {{0.1, 0.3, 0.5}, {0.2, 0.4, 0.6}}));
    network.put("b1", Nd4j.create(new double[] {0.1, 0.2, 0.3}));
    network.put("W2", Nd4j.create(new double[][] {{0.1, 0.4}, {0.2, 0.5}, {0.3, 0.6}}));
    network.put("b2", Nd4j.create(new double[] {0.1, 0.2}));
    network.put("W3", Nd4j.create(new double[][] {{0.1, 0.3}, {0.2, 0.4}}));
    network.put("b3", Nd4j.create(new double[] {0.1, 0.2}));
    return network;
}

public static INDArray forward(Map<String, INDArray> network, INDArray x) {
    INDArray W1 = network.get("W1");
    INDArray W2 = network.get("W2");
    INDArray W3 = network.get("W3");
    INDArray b1 = network.get("b1");
    INDArray b2 = network.get("b2");
    INDArray b3 = network.get("b3");

    INDArray a1 = x.mmul(W1).add(b1);
    INDArray z1 = Transforms.sigmoid(a1);
    INDArray a2 = z1.mmul(W2).add(b2);
    INDArray z2 = Transforms.sigmoid(a2);
    INDArray a3 = z2.mmul(W3).add(b3);
    INDArray y = Transforms.identity(a3);
    return y;
}

Map<String, INDArray> network = init_network();
INDArray x = Nd4j.create(new double[] {1.0, 0.5});
INDArray y = forward(network, x);
assertEquals("[0.32,0.70]", Util.string(y));

INDArray a = Nd4j.create(new double[] {0.3, 2.9, 4.0});
//
INDArray exp_a = Transforms.exp(a);
assertEquals("[1.35,18.17,54.60]", Util.string(exp_a));
//
Number sum_exp_a = exp_a.sumNumber();
assertEquals(74.1221542102, sum_exp_a.doubleValue(), 5e-6);
//3.4.3 Implementierungszusammenfassung 3.5 Design der Ausgabeschicht 3.5.1 Exponentialfunktion mit gleicher Funktion und Softmax-Funktion Summe der Exponentialfunktion Softmax-Funktion
INDArray y = exp_a.div(sum_exp_a);
assertEquals("[0.02,0.25,0.74]", Util.string(y));

3.5.2 Vorsichtsmaßnahmen zur Implementierung der Softmax-Funktion

public static INDArray softmax_wrong(INDArray a) {
    INDArray exp_a = Transforms.exp(a);
    Number sum_exp_a = exp_a.sumNumber();
    INDArray y = exp_a.div(sum_exp_a);
    return y;
}

public static INDArray softmax_right(INDArray a) {
    Number c = a.maxNumber();
    INDArray exp_a = Transforms.exp(a.sub(c));
    Number sum_exp_a = exp_a.sumNumber();
    INDArray y = exp_a.div(sum_exp_a);
    return y;
}

INDArray a = Nd4j.create(new double[] {1010, 1000, 990});
//Nicht richtig berechnet
assertEquals("[NaN,NaN,NaN]", Util.string(Transforms.exp(a).div(Transforms.exp(a).sumNumber())));
Number c = a.maxNumber();
assertEquals("[0.00,-10.00,-20.00]", Util.string(a.sub(c)));
assertEquals("[1.00,0.00,0.00]", Util.string(Transforms.exp(a.sub(c)).div(Transforms.exp(a.sub(c)).sumNumber())));

//Fehler
assertEquals("[NaN,NaN,NaN]", Util.string(softmax_wrong(a)));
//richtig
assertEquals("[1.00,0.00,0.00]", Util.string(softmax_right(a)));
//Korrigieren Sie Softmax für ND4J(INDArray)Ist vorbereitet.
assertEquals("[1.00,0.00,0.00]", Util.string(Transforms.softmax(a)));

3.5.3 Merkmale der Softmax-Funktion

INDArray a = Nd4j.create(new double[] {0.3, 2.9, 4.0});
INDArray y = Transforms.softmax(a);
assertEquals("[0.02,0.25,0.74]", Util.string(y));
//Die Summe ist 1.
assertEquals(1.0, y.sumNumber().doubleValue(), 5e-6);

3.6 Handschriftliche Nummernerkennung

3.6.1 MNIST-Datensatz

MNIST-Daten wird von [MNISTImages-Klasse] gelesen (https://github.com/saka1029/Deep.Learning/blob/master/src/main) /java/deep/learning/common/MNISTImages.java). Diese Klasse liest die Datei nach dem Herunterladen und Dekomprimieren der Daten in DIE MNIST-DATENBANK handgeschriebener Ziffern.

//Laden Sie das MNIST-Dataset in die MNISTImages-Klasse.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
MNISTImages test = new MNISTImages(Constants.TestImages, Constants.TestLabels);
assertEquals(60000, train.size);
assertEquals(784, train.imageSize);
assertEquals(10000, test.size);
assertEquals(784, test.imageSize);

//Die ersten 100 Bilder der Trainingsdaten werden als PNG ausgegeben.
if (!Constants.TrainImagesOutput.exists())
    Constants.TrainImagesOutput.mkdirs();
for (int i = 0; i < 100; ++i) {
    File image = new File(Constants.TrainImagesOutput,
        String.format("%05d-%d.png ", i, train.label(i)));
    train.writePngFile(i, image);
}

Das tatsächliche Ausgabebild ist wie folgt. Der Dateiname lautet "[Seriennummer 5 Ziffern] - [Label] .png ".

00000-5.png 00000-5.png 00001-0.png 00001-0.png 00002-4.png 00002-4.png 00003-1.png 00003-1.png 00004-9.png 00004-9.png 00005-2.png 00005-2.png 00006-1.png 00006-1.png 00007-3.png 00007-3.png 00008-1.png 00008-1.png 00009-4.png 00009-4.png .....

3.6.2 Inferenzverarbeitung für neuronale Netze

Beispielgewichtsdaten (sample_weight.pkl) ist [SampleWeight](https://github.com/ saka1029 / Deep.Learning / blob / master / src / main / java / deep / learning / common / SampleWeight.java) Laden mit der Klasse. Sample_weight.pkl sind jedoch in Python serialisierte Daten und können in Java nicht direkt gelesen werden. Daher wird sample_weight.pkl einmal mit dem folgenden Python-Programm in Text konvertiert. Die Textdaten sind SampleWeight.txt. Die Klasse SampleWeight liest diese Textdatei.

sample_weight.py


import pickle
import numpy

pkl = "sample_weight.pkl"
with open(pkl, "rb") as f:
	network = pickle.load(f)
for k, v in network.items():
    print(k, end="")
    dim = v.ndim
    for d in v.shape:
        print("", d, end="")
    print()
    for e in v.flatten():
        print(e)

Der Code, der tatsächlich liest und schließt, lautet wie folgt. Die Erkennungsgenauigkeit ist [Deep Learning von Grund auf neu gemacht] Es ist 93,52%, was dem entspricht (https://www.oreilly.co.jp/books/9784873117584/).

static INDArray normalize(byte[][] images) {
    int imageCount = images.length;
    int imageSize = images[0].length;
    INDArray norm = Nd4j.create(imageCount, imageSize);
    for (int i = 0; i < imageCount; ++i)
        for (int j = 0; j < imageSize; ++j)
            norm.putScalar(i, j, (images[i][j] & 0xff) / 255.0);
    return norm;
}

static INDArray predict(Map<String, INDArray> network, INDArray x) {
    INDArray W1 = network.get("W1");
    INDArray W2 = network.get("W2");
    INDArray W3 = network.get("W3");
    INDArray b1 = network.get("b1");
    INDArray b2 = network.get("b2");
    INDArray b3 = network.get("b3");

    //Wenn Sie Folgendes tun, tritt bei der Stapelverarbeitung ein Fehler auf.
    // INDArray a1 = x.mmul(W1).add(b1);
    // x.mmul(W1)Dies liegt daran, dass b1 eindimensional ist, obwohl das Ergebnis ein zweidimensionales Array ist.
    // add(INDArray)Sendet nicht automatisch.
    //Es kann auch explizit wie folgt gesendet werden:
    // INDArray a1 = x.mmul(W1).add(b1.broadcast(x.size(0), b1.size(1)));
    INDArray a1 = x.mmul(W1).addRowVector(b1);
    INDArray z1 = Transforms.sigmoid(a1);
    INDArray a2 = z1.mmul(W2).addRowVector(b2);
    INDArray z2 = Transforms.sigmoid(a2);
    INDArray a3 = z2.mmul(W3).addRowVector(b3);
    INDArray y = Transforms.softmax(a3);

    return y;
}

//Laden Sie das Testbild.
MNISTImages test = new MNISTImages(Constants.TestImages, Constants.TestLabels);
//Laden Sie die Probengewichtsdaten.
Map<String, INDArray> network = SampleWeight.read(Constants.SampleWeights);
//Normalisieren Sie das Bild(0-255 -> 0.0-1.0)
INDArray x = test.normalizedImages();
int size = x.size(0);
int accuracy_cnt = 0;
for (int i = 0; i < size; ++i) {
    INDArray y = predict(network, x.getRow(i));
    //Das letzte Argument, 1, repräsentiert die Dimension.
    INDArray max = Nd4j.getExecutioner().exec(new IAMax(y), 1);
    if (max.getInt(0) == test.label(i))
        ++accuracy_cnt;
}
//        System.out.printf("Accuracy:%f%n", (double) accuracy_cnt / size);
assertEquals(10000, size);
assertEquals(9352, accuracy_cnt);

3.6.3 Stapelverarbeitung

NDArrayIndex zum Abrufen von Daten zur Stapelgröße von INDArray Verwenden Sie doc / org / nd4j / linalg / indexing / NDArrayIndex.html). NDArrayIndex. Sie können die Elemente von Anfang bis Ende mit Intervall abrufen (int start, int end). (Start ≤ i <Ende). Sie können auch die Klasse IAMax verwenden, um dasselbe wie die Argmax-Funktion von NumPy zu tun.

int batch_size = 100;
//Laden Sie das Testbild.
MNISTImages test = new MNISTImages(Constants.TestImages, Constants.TestLabels);
//Laden Sie die Probengewichtsdaten.
Map<String, INDArray> network = SampleWeight.read(Constants.SampleWeights);
//Normalisieren Sie das Bild(0-255 -> 0.0-1.0)
INDArray x = test.normalizedImages();
int size = x.size(0);
int accuracy_cnt = 0;
for (int i = 0; i < size; i += batch_size) {
    //Extrahieren Sie das Bild für die Stapelgröße und sagen Sie es voraus()Wird genannt.
    INDArray y = predict(network, x.get(NDArrayIndex.interval(i, i + batch_size)));
    //Das letzte Argument, 1, repräsentiert die Dimension.
    INDArray max = Nd4j.getExecutioner().exec(new IAMax(y), 1);
    for (int j = 0; j < batch_size; ++j)
        if (max.getInt(j) == test.label(i + j))
            ++accuracy_cnt;
}
//        System.out.printf("Accuracy:%f%n", (double) accuracy_cnt / size);
assertEquals(10000, size);
assertEquals(9352, accuracy_cnt);

In meiner Umgebung konnte ich durch die Stapelverarbeitung von 2,7 Sekunden auf 0,5 Sekunden beschleunigen. Ich benutze keine GPU.

Recommended Posts

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
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
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