static class MulLayer {
private double x, y;
public double forward(double x, double y) {
this.x = x;
this.y = y;
return x * y;
}
//Da Java nicht mehrere Werte zurückgeben kann, wird es als Array zurückgegeben.
public double[] backward(double dout) {
return new double[] {dout * y, dout * x};
}
}
double apple = 100;
double apple_num = 2;
double tax = 1.1;
// Layer
MulLayer mul_apple_layer = new MulLayer();
MulLayer mul_tax_layer = new MulLayer();
// forward
double apple_price = mul_apple_layer.forward(apple, apple_num);
double price = mul_tax_layer.forward(apple_price, tax);
assertEquals(220.0, price, 5e-6);
// backward
double dprice = 1;
double[] dapple_price_tax = mul_tax_layer.backward(dprice);
double[] dapple_num = mul_apple_layer.backward(dapple_price_tax[0]);
assertEquals(2.2, dapple_num[0], 5e-6);
assertEquals(110.0, dapple_num[1], 5e-6);
assertEquals(200.0, dapple_price_tax[1], 5e-6);
static class AddLayer {
public double forward(double x, double y) {
return x + y;
}
public double[] backward(double dout) {
return new double[] {dout, dout};
}
}
double apple = 100;
double apple_num = 2;
double orange = 150;
double orange_num = 3;
double tax = 1.1;
// Layer
MulLayer mul_apple_layer = new MulLayer();
MulLayer mul_orange_layer = new MulLayer();
AddLayer add_apple_orange_layer = new AddLayer();
MulLayer mul_tax_layer = new MulLayer();
// forward
double apple_price = mul_apple_layer.forward(apple, apple_num);
double orange_price = mul_orange_layer.forward(orange, orange_num);
double all_price = add_apple_orange_layer.forward(apple_price, orange_price);
double price = mul_tax_layer.forward(all_price, tax);
// backward
double dprice = 1;
double[] dall_price = mul_tax_layer.backward(dprice);
double[] dapple_dorange_price = add_apple_orange_layer.backward(dall_price[0]);
double[] dorange = mul_orange_layer.backward(dapple_dorange_price[1]);
double[] dapple = mul_apple_layer.backward(dapple_dorange_price[0]);
assertEquals(715.0, price, 5e-6);
assertEquals(110.0, dapple[1], 5e-6);
assertEquals(2.2, dapple[0], 5e-6);
assertEquals(3.3, dorange[0], 5e-6);
assertEquals(165.0, dorange[1], 5e-6);
assertEquals(650.0, dall_price[1], 5e-6);
Die Klassenimplementierung des ReLU-Beispiels unter Verwendung der Fehlerrückverbreitungsmethode lautet Relu. ) ist.
INDArray x = Nd4j.create(new double[][] {{1.0, -0.5}, {-2.0, 3.0}});
assertEquals("[[1.00,-0.50],[-2.00,3.00]]", Util.string(x));
//Der Test unterscheidet sich von diesem Buch.
Relu relu = new Relu();
INDArray a = relu.forward(x);
//Ergebnis vorwärts
assertEquals("[[1.00,0.00],[0.00,3.00]]", Util.string(a));
// mask
assertEquals("[[1.00,0.00],[0.00,1.00]]", Util.string(relu.mask));
INDArray dout = Nd4j.create(new double[][] {{5, 6}, {7, 8}});
INDArray b = relu.backward(dout);
//Ergebnisse von rückwärts
assertEquals("[[5.00,0.00],[0.00,8.00]]", Util.string(b));
Die Implementierungsklasse der Sigmoid-Schicht, die die Fehlerrückvermehrungsmethode verwendet, ist Sigmoid ) ist.
Die Implementierungsklasse der Affine-Ebene, die die Fehlerrückverteilungsmethode verwendet, ist Affine ) ist.
try (Random r = new DefaultRandom()) {
INDArray X = r.nextGaussian(new int[] {2});
INDArray W = r.nextGaussian(new int[] {2, 3});
INDArray B = r.nextGaussian(new int[] {3});
assertArrayEquals(new int[] {1, 2}, X.shape());
assertArrayEquals(new int[] {2, 3}, W.shape());
assertArrayEquals(new int[] {1, 3}, B.shape());
INDArray Y = X.mmul(W).addRowVector(B);
assertArrayEquals(new int[] {1, 3}, Y.shape());
}
INDArray X_dot_W = Nd4j.create(new double[][] {{0, 0, 0}, {10, 10, 10}});
INDArray B = Nd4j.create(new double[] {1, 2, 3});
assertEquals("[[0.00,0.00,0.00],[10.00,10.00,10.00]]", Util.string(X_dot_W));
assertEquals("[[1.00,2.00,3.00],[11.00,12.00,13.00]]", Util.string(X_dot_W.addRowVector(B)));
Die Implementierungsklasse der Softmax-with-Loss-Schicht unter Verwendung der Fehlerrückverteilungsmethode ist [SoftmaxWithLoss](https://github.com/saka1029/Deep.Learning/blob/master/src/main/java/deep/learning/common /SoftmaxWithLoss.java).
Die Klasse des zweischichtigen neuronalen Netzwerks, das die Fehlerrückverbreitungsmethode verwendet, ist TwoLayerNet. Java). Es gibt auch TwoLayerNet in Kapitel 4, dies ist jedoch eine numerische Unterscheidung. Es ist derjenige, der verwendet. Um die Handhabung jeder Schicht in dieser Implementierung zu vereinfachen, werden die folgenden zwei Schnittstellen definiert.
public interface Layer {
INDArray forward(INDArray x);
INDArray backward(INDArray x);
}
public interface LastLayer {
double forward(INDArray x, INDArray t);
INDArray backward(INDArray x);
}
Der Gradientenvergleich mit der numerischen Differenzierungsmethode ist erheblich größer als der in diesem Buch beschriebene. Daher wird es hier mit dem Gradienten durch numerische Differenzierung geteilt durch 3 verglichen. Functions.average (INDArray) ist der Durchschnitt aller Elemente Die gewünschte Methode.
public static double average(INDArray x) {
// x.length()Gibt die Gesamtzahl der Elemente zurück.
return x.sumNumber().doubleValue() / x.length();
}
//Lesen Sie die Trainingsdaten von MNIST.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
TwoLayerNet network = new TwoLayerNet(784, 50, 10);
//Normalisiertes Bild und eins-Nehmen Sie jeweils die ersten 3 heißen Etiketten heraus.
int batch_size = 3;
INDArray x_batch = train.normalizedImages().get(NDArrayIndex.interval(0, batch_size));
INDArray t_batch = train.oneHotLabels().get(NDArrayIndex.interval(0, batch_size));
//Finden Sie den Gradienten durch numerische Differenzierung.
Params grad_numerical = network.numerical_gradient(x_batch, t_batch);
//Ermitteln Sie den Gradienten mithilfe der Fehlerausbreitungsmethode.
Params grad_backprop = network.gradient(x_batch, t_batch);
//Vergleichen Sie die Ergebnisse der numerischen Differenzierung und der Fehlerausbreitungsmethode.
double diff_W1 = Functions.average(Transforms.abs(grad_backprop.get("W1").sub(grad_numerical.get("W1"))));
double diff_b1 = Functions.average(Transforms.abs(grad_backprop.get("b1").sub(grad_numerical.get("b1"))));
double diff_W2 = Functions.average(Transforms.abs(grad_backprop.get("W2").sub(grad_numerical.get("W2"))));
double diff_b2 = Functions.average(Transforms.abs(grad_backprop.get("b2").sub(grad_numerical.get("b2"))));
System.out.println("W1=" + diff_W1);
System.out.println("b1=" + diff_b1);
System.out.println("W2=" + diff_W2);
System.out.println("b2=" + diff_b2);
//Der Unterschied ist etwas größer als dieses Buch.
assertTrue(diff_b1 < 1e-3);
assertTrue(diff_W2 < 1e-3);
assertTrue(diff_b2 < 1e-3);
assertTrue(diff_W1 < 1e-3);
Es ist erheblich schneller als das Lernen mit numerischer Differenzierung. In meiner Umgebung sind 10000 Schleifen in ungefähr 89 Sekunden abgeschlossen. Die endgültige Erkennungsgenauigkeit betrug jedoch etwa 84%, was der numerischen Differenzierung unterlegen war. Wahrscheinlich liegt irgendwo in der Schichtimplementierung ein Fehler vor, da der Unterschied beim Vergleich des Gradienten mit dem numerischen Differentialfall groß ist.
//Lesen Sie die Trainingsdaten von MNIST.
MNISTImages train = new MNISTImages(Constants.TrainImages, Constants.TrainLabels);
INDArray x_train = train.normalizedImages();
INDArray t_train = train.oneHotLabels();
//Lesen Sie die MNIST-Testdaten.
MNISTImages test = new MNISTImages(Constants.TestImages, Constants.TestLabels);
INDArray x_test = test.normalizedImages();
INDArray t_test = test.oneHotLabels();
TwoLayerNet network = new TwoLayerNet(784, 50, 10);
DataSet dataSet = new DataSet(x_train, t_train);
int iters_num = 10000;
int train_size = x_train.size(0);
int batch_size = 100;
double learning_rate = 0.1;
List<Double> train_loss_list = new ArrayList<>();
List<Double> train_acc_list = new ArrayList<>();
List<Double> test_acc_list = new ArrayList<>();
int iter_per_epoch = Math.max(train_size / batch_size, 1);
for (int i = 0; i < iters_num; ++i) {
DataSet sample = dataSet.sample(batch_size);
INDArray x_batch = sample.getFeatures();
INDArray t_batch = sample.getLabels();
//Ermitteln Sie den Gradienten mithilfe der Methode zur Fehlerrückübertragung
Params grad = network.gradient(x_batch, t_batch);
//aktualisieren
network.params.update((p, a) -> p.subi(a.mul(learning_rate)), grad);
double loss = network.loss(x_batch, t_batch);
train_loss_list.add(loss);
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("loss=%f train_acc=%f test_acc=%f%n", loss, train_acc, test_acc);
}
}
assertTrue(train_acc_list.get(train_acc_list.size() - 1) > 0.8);
assertTrue(test_acc_list.get(test_acc_list.size() - 1) > 0.8);
Recommended Posts