diff --git a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNN1DGradientCheckTest.java b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNN1DGradientCheckTest.java index 44d62a952702..da26fb9760cf 100644 --- a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNN1DGradientCheckTest.java +++ b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNN1DGradientCheckTest.java @@ -34,6 +34,76 @@ public class CNN1DGradientCheckTest extends BaseDL4JTest { Nd4j.setDataType(DataBuffer.Type.DOUBLE); } + @Test + public void testCnn1DWithLocallyConnected1D() { + Nd4j.getRandom().setSeed(1337); + + int[] minibatchSizes = {2, 3}; + int length = 7; + int convNIn = 2; + int convNOut1 = 3; + int convNOut2 = 4; + int finalNOut = 4; + + int[] kernels = {1}; + int stride = 1; + int padding = 0; + + Activation[] activations = {Activation.SIGMOID}; + + for (Activation afn : activations) { + for (int minibatchSize : minibatchSizes) { + for (int kernel : kernels) { + INDArray input = Nd4j.rand(new int[]{minibatchSize, convNIn, length}); + INDArray labels = Nd4j.zeros(minibatchSize, finalNOut, length); + for (int i = 0; i < minibatchSize; i++) { + for (int j = 0; j < length; j++) { + labels.putScalar(new int[]{i, i % finalNOut, j}, 1.0); + } + } + + MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder() + .updater(new NoOp()).weightInit(WeightInit.DISTRIBUTION) + .dist(new NormalDistribution(0, 1)).convolutionMode(ConvolutionMode.Same).list() + .layer(new Convolution1DLayer.Builder().activation(afn).kernelSize(kernel) + .stride(stride).padding(padding).nIn(convNIn).nOut(convNOut1) + .build()) + .layer(new LocallyConnected1D.Builder().activation(afn).kernelSize(kernel) + .stride(stride).padding(padding).nIn(convNOut1).nOut(convNOut2).hasBias(false) + .build()) + .layer(new RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT) + .activation(Activation.SOFTMAX).nOut(finalNOut).build()) + .setInputType(InputType.recurrent(convNIn, length)).build(); + + String json = conf.toJson(); + MultiLayerConfiguration c2 = MultiLayerConfiguration.fromJson(json); + assertEquals(conf, c2); + + MultiLayerNetwork net = new MultiLayerNetwork(conf); + net.init(); + + String msg = "Minibatch=" + minibatchSize + ", activationFn=" + + afn + ", kernel = " + kernel; + + if (PRINT_RESULTS) { + System.out.println(msg); + for (int j = 0; j < net.getnLayers(); j++) + System.out.println("Layer " + j + " # params: " + net.getLayer(j).numParams()); + } + + boolean gradOK = GradientCheckUtil.checkGradients(net, DEFAULT_EPS, DEFAULT_MAX_REL_ERROR, + DEFAULT_MIN_ABS_ERROR, PRINT_RESULTS, RETURN_ON_FIRST_FAILURE, input, labels); + + assertTrue(msg, gradOK); + + TestUtils.testModelSerialization(net); + } + + } + } + } + + @Test public void testCnn1DWithCropping1D() { Nd4j.getRandom().setSeed(1337); @@ -55,18 +125,18 @@ public void testCnn1DWithCropping1D() { Activation[] activations = {Activation.SIGMOID}; SubsamplingLayer.PoolingType[] poolingTypes = - new SubsamplingLayer.PoolingType[] {SubsamplingLayer.PoolingType.MAX, + new SubsamplingLayer.PoolingType[]{SubsamplingLayer.PoolingType.MAX, SubsamplingLayer.PoolingType.AVG, SubsamplingLayer.PoolingType.PNORM}; for (Activation afn : activations) { for (SubsamplingLayer.PoolingType poolingType : poolingTypes) { for (int minibatchSize : minibatchSizes) { for (int kernel : kernels) { - INDArray input = Nd4j.rand(new int[] {minibatchSize, convNIn, length}); + INDArray input = Nd4j.rand(new int[]{minibatchSize, convNIn, length}); INDArray labels = Nd4j.zeros(minibatchSize, finalNOut, croppedLength); for (int i = 0; i < minibatchSize; i++) { for (int j = 0; j < croppedLength; j++) { - labels.putScalar(new int[] {i, i % finalNOut, j}, 1.0); + labels.putScalar(new int[]{i, i % finalNOut, j}, 1.0); } } @@ -135,18 +205,18 @@ public void testCnn1DWithZeroPadding1D() { Activation[] activations = {Activation.SIGMOID}; SubsamplingLayer.PoolingType[] poolingTypes = - new SubsamplingLayer.PoolingType[] {SubsamplingLayer.PoolingType.MAX, + new SubsamplingLayer.PoolingType[]{SubsamplingLayer.PoolingType.MAX, SubsamplingLayer.PoolingType.AVG, SubsamplingLayer.PoolingType.PNORM}; for (Activation afn : activations) { for (SubsamplingLayer.PoolingType poolingType : poolingTypes) { for (int minibatchSize : minibatchSizes) { for (int kernel : kernels) { - INDArray input = Nd4j.rand(new int[] {minibatchSize, convNIn, length}); + INDArray input = Nd4j.rand(new int[]{minibatchSize, convNIn, length}); INDArray labels = Nd4j.zeros(minibatchSize, finalNOut, paddedLength); for (int i = 0; i < minibatchSize; i++) { for (int j = 0; j < paddedLength; j++) { - labels.putScalar(new int[] {i, i % finalNOut, j}, 1.0); + labels.putScalar(new int[]{i, i % finalNOut, j}, 1.0); } } @@ -213,35 +283,35 @@ public void testCnn1DWithSubsampling1D() { Activation[] activations = {Activation.SIGMOID, Activation.TANH}; SubsamplingLayer.PoolingType[] poolingTypes = - new SubsamplingLayer.PoolingType[] {SubsamplingLayer.PoolingType.MAX, - SubsamplingLayer.PoolingType.AVG, SubsamplingLayer.PoolingType.PNORM}; + new SubsamplingLayer.PoolingType[]{SubsamplingLayer.PoolingType.MAX, + SubsamplingLayer.PoolingType.AVG, SubsamplingLayer.PoolingType.PNORM}; for (Activation afn : activations) { for (SubsamplingLayer.PoolingType poolingType : poolingTypes) { for (int minibatchSize : minibatchSizes) { for (int kernel : kernels) { - INDArray input = Nd4j.rand(new int[] {minibatchSize, convNIn, length}); + INDArray input = Nd4j.rand(new int[]{minibatchSize, convNIn, length}); INDArray labels = Nd4j.zeros(minibatchSize, finalNOut, length); for (int i = 0; i < minibatchSize; i++) { for (int j = 0; j < length; j++) { - labels.putScalar(new int[] {i, i % finalNOut, j}, 1.0); + labels.putScalar(new int[]{i, i % finalNOut, j}, 1.0); } } MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder() - .updater(new NoOp()).weightInit(WeightInit.DISTRIBUTION) - .dist(new NormalDistribution(0, 1)).convolutionMode(ConvolutionMode.Same).list() - .layer(0, new Convolution1DLayer.Builder().activation(afn).kernelSize(kernel) - .stride(stride).padding(padding).nIn(convNIn).nOut(convNOut1) - .build()) - .layer(1, new Convolution1DLayer.Builder().activation(afn).kernelSize(kernel) - .stride(stride).padding(padding).nIn(convNOut1).nOut(convNOut2) - .build()) - .layer(2, new Subsampling1DLayer.Builder(poolingType).kernelSize(kernel) - .stride(stride).padding(padding).pnorm(pnorm).build()) - .layer(3, new RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT) - .activation(Activation.SOFTMAX).nOut(finalNOut).build()) - .setInputType(InputType.recurrent(convNIn, length)).build(); + .updater(new NoOp()).weightInit(WeightInit.DISTRIBUTION) + .dist(new NormalDistribution(0, 1)).convolutionMode(ConvolutionMode.Same).list() + .layer(0, new Convolution1DLayer.Builder().activation(afn).kernelSize(kernel) + .stride(stride).padding(padding).nIn(convNIn).nOut(convNOut1) + .build()) + .layer(1, new Convolution1DLayer.Builder().activation(afn).kernelSize(kernel) + .stride(stride).padding(padding).nIn(convNOut1).nOut(convNOut2) + .build()) + .layer(2, new Subsampling1DLayer.Builder(poolingType).kernelSize(kernel) + .stride(stride).padding(padding).pnorm(pnorm).build()) + .layer(3, new RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT) + .activation(Activation.SOFTMAX).nOut(finalNOut).build()) + .setInputType(InputType.recurrent(convNIn, length)).build(); String json = conf.toJson(); MultiLayerConfiguration c2 = MultiLayerConfiguration.fromJson(json); @@ -251,7 +321,7 @@ public void testCnn1DWithSubsampling1D() { net.init(); String msg = "PoolingType=" + poolingType + ", minibatch=" + minibatchSize + ", activationFn=" - + afn + ", kernel = " + kernel; + + afn + ", kernel = " + kernel; if (PRINT_RESULTS) { System.out.println(msg); @@ -260,7 +330,7 @@ public void testCnn1DWithSubsampling1D() { } boolean gradOK = GradientCheckUtil.checkGradients(net, DEFAULT_EPS, DEFAULT_MAX_REL_ERROR, - DEFAULT_MIN_ABS_ERROR, PRINT_RESULTS, RETURN_ON_FIRST_FAILURE, input, labels); + DEFAULT_MIN_ABS_ERROR, PRINT_RESULTS, RETURN_ON_FIRST_FAILURE, input, labels); assertTrue(msg, gradOK); TestUtils.testModelSerialization(net); diff --git a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNNGradientCheckTest.java b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNNGradientCheckTest.java index 8a5be4a4c2dc..01da056ee220 100644 --- a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNNGradientCheckTest.java +++ b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/gradientcheck/CNNGradientCheckTest.java @@ -549,58 +549,55 @@ public void testCnnLocallyConnected2D() { int[] inputDepths = {1, 2, 4}; Activation[] activations = {Activation.SIGMOID, Activation.TANH}; - SubsamplingLayer.PoolingType[] poolingTypes = new SubsamplingLayer.PoolingType[]{ - SubsamplingLayer.PoolingType.MAX, SubsamplingLayer.PoolingType.AVG}; Nd4j.getRandom().setSeed(12345); for (int inputDepth : inputDepths) { for (Activation afn : activations) { - for (SubsamplingLayer.PoolingType poolingType : poolingTypes) { - for (int minibatchSize : minibatchSizes) { - INDArray input = Nd4j.rand(minibatchSize, width * height * inputDepth); - INDArray labels = Nd4j.zeros(minibatchSize, nOut); - for (int i = 0; i < minibatchSize; i++) { - labels.putScalar(new int[]{i, i % nOut}, 1.0); - } - - MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(12345).updater(new NoOp()) - .activation(afn) - .list() - .layer(0, new ConvolutionLayer.Builder().kernelSize(2, 2).stride(1, 1) - .padding(0, 0).nIn(inputDepth).nOut(2).build())//output: (5-2+0)/1+1 = 4 - .layer(1, new LocallyConnected2D.Builder().nIn(2).nOut(7).kernelSize(2, 2) - .setInputSize(4, 4).convolutionMode(ConvolutionMode.Strict).hasBias(false) - .stride(1, 1).padding(0, 0).build()) //(4-2+0)/1+1 = 3 - .layer(2, new ConvolutionLayer.Builder().nIn(7).nOut(2).kernelSize(2, 2) - .stride(1, 1).padding(0, 0).build()) //(3-2+0)/1+1 = 2 - .layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.MCXENT) - .activation(Activation.SOFTMAX).nIn(2 * 2 * 2).nOut(nOut) - .build()) - .setInputType(InputType.convolutionalFlat(height, width, inputDepth)).build(); + for (int minibatchSize : minibatchSizes) { + INDArray input = Nd4j.rand(minibatchSize, width * height * inputDepth); + INDArray labels = Nd4j.zeros(minibatchSize, nOut); + for (int i = 0; i < minibatchSize; i++) { + labels.putScalar(new int[]{i, i % nOut}, 1.0); + } - assertEquals(ConvolutionMode.Truncate, - ((ConvolutionLayer) conf.getConf(0).getLayer()).getConvolutionMode()); + MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(12345).updater(new NoOp()) + .activation(afn) + .list() + .layer(0, new ConvolutionLayer.Builder().kernelSize(2, 2).stride(1, 1) + .padding(0, 0).nIn(inputDepth).nOut(2).build())//output: (5-2+0)/1+1 = 4 + .layer(1, new LocallyConnected2D.Builder().nIn(2).nOut(7).kernelSize(2, 2) + .setInputSize(4, 4).convolutionMode(ConvolutionMode.Strict).hasBias(false) + .stride(1, 1).padding(0, 0).build()) //(4-2+0)/1+1 = 3 + .layer(2, new ConvolutionLayer.Builder().nIn(7).nOut(2).kernelSize(2, 2) + .stride(1, 1).padding(0, 0).build()) //(3-2+0)/1+1 = 2 + .layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.MCXENT) + .activation(Activation.SOFTMAX).nIn(2 * 2 * 2).nOut(nOut) + .build()) + .setInputType(InputType.convolutionalFlat(height, width, inputDepth)).build(); + + assertEquals(ConvolutionMode.Truncate, + ((ConvolutionLayer) conf.getConf(0).getLayer()).getConvolutionMode()); - MultiLayerNetwork net = new MultiLayerNetwork(conf); - net.init(); + MultiLayerNetwork net = new MultiLayerNetwork(conf); + net.init(); - for (int i = 0; i < 4; i++) { - System.out.println("nParams, layer " + i + ": " + net.getLayer(i).numParams()); - } + for (int i = 0; i < 4; i++) { + System.out.println("nParams, layer " + i + ": " + net.getLayer(i).numParams()); + } - String msg = "PoolingType=" + poolingType + ", minibatch=" + minibatchSize + ", activationFn=" - + afn; - System.out.println(msg); + String msg = "Minibatch=" + minibatchSize + ", activationFn=" + + afn; + System.out.println(msg); - boolean gradOK = GradientCheckUtil.checkGradients(net, DEFAULT_EPS, DEFAULT_MAX_REL_ERROR, - DEFAULT_MIN_ABS_ERROR, PRINT_RESULTS, RETURN_ON_FIRST_FAILURE, input, labels); + boolean gradOK = GradientCheckUtil.checkGradients(net, DEFAULT_EPS, DEFAULT_MAX_REL_ERROR, + DEFAULT_MIN_ABS_ERROR, PRINT_RESULTS, RETURN_ON_FIRST_FAILURE, input, labels); - assertTrue(msg, gradOK); + assertTrue(msg, gradOK); - TestUtils.testModelSerialization(net); - } + TestUtils.testModelSerialization(net); } + } } } @@ -911,7 +908,7 @@ public void testDeconvolution2D() { int[] kernelSizes = new int[]{1, 1, 3, 3, 1, 1, 3, 3}; int[] strides = {1, 1, 1, 1, 2, 2, 2, 2}; int[] dilation = {1, 2, 2, 1, 1, 1, 2, 2}; - Activation[] activations = new Activation[]{Activation.SIGMOID, Activation.TANH, Activation.TANH, Activation.TANH, Activation.TANH, Activation.SIGMOID, Activation.SIGMOID, Activation.SIGMOID}; + Activation[] activations = new Activation[]{Activation.SIGMOID, Activation.TANH, Activation.TANH, Activation.TANH, Activation.TANH, Activation.SIGMOID, Activation.SIGMOID, Activation.SIGMOID}; ConvolutionMode[] cModes = new ConvolutionMode[]{Truncate, Truncate, Truncate, Truncate, Truncate, Truncate, Truncate, Truncate}; int width = 7; int height = 7; @@ -976,7 +973,7 @@ public void testDeconvolution2D() { public void testSeparableConv2D() { int nOut = 2; - int[] minibatchSizes = new int[] {1, 3}; + int[] minibatchSizes = new int[]{1, 3}; int width = 8; int height = 8; int inputDepth = 3; @@ -987,13 +984,13 @@ public void testSeparableConv2D() { Nd4j.getRandom().setSeed(12345); - int[] ks = new int[]{1,3,1,3,1,3,1,3}; - int[] ss = new int[]{1,1,2,2,1,1,2,2}; - int[] ds = new int[]{1,1,1,1,2,2,2,2}; + int[] ks = new int[]{1, 3, 1, 3, 1, 3, 1, 3}; + int[] ss = new int[]{1, 1, 2, 2, 1, 1, 2, 2}; + int[] ds = new int[]{1, 1, 1, 1, 2, 2, 2, 2}; ConvolutionMode[] cms = new ConvolutionMode[]{Truncate, Truncate, Truncate, Truncate, Truncate, Truncate, Truncate, Truncate}; - int[] mb = new int[]{1,1,3,3,3,1,3,3}; + int[] mb = new int[]{1, 1, 3, 3, 3, 1, 3, 3}; - for( int t=0; t 0 (strideH = " + sH + ", strideW = " + sW + ")" + "\n" + getConfigErrorCommonLastLine(inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); } if (kH <= 0 || kH > inHeight + 2 * padH) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, true) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + " Invalid input configuration for kernel height. Require 0 < kH <= inHeight + 2*padH; got (kH=" + kH + ", inHeight=" + inHeight + ", padH=" + padH + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); } if (kW <= 0 || kW > inWidth + 2 * padW) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + " Invalid input configuration for kernel width. Require 0 < kW <= inWidth + 2*padW; got (kW=" + kW + ", inWidth=" + inWidth + ", padW=" + padW + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); @@ -119,7 +119,7 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern int sW = stride[1]; if (sH <= 0 || sW <= 0 || sD <= 0) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, sH <= 0) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, sH <= 0) + " Invalid strides: strides must be > 0 (strideH = " + sH + ", strideW = " + sW + ", strideD = " + sD + ")" + "\n" + getConfigErrorCommonLastLine(inputType, kernelSize, stride, padding, outputChannels, @@ -127,20 +127,20 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern } if (kH <= 0 || kH > inHeight + 2 * padH) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, true) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + " Invalid input configuration for kernel height. Require 0 < kH <= inHeight + 2*padH; got (kH=" + kH + ", inHeight=" + inHeight + ", padH=" + padH + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputChannels, convolutionMode)); } if (kW <= 0 || kW > inWidth + 2 * padW) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + " Invalid input configuration for kernel width. Require 0 < kW <= inWidth + 2*padW; got (kW=" + kW + ", inWidth=" + inWidth + ", padW=" + padW + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputChannels, convolutionMode)); } if (kD <= 0 || kD > inDepth + 2 * padD) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + " Invalid input configuration for kernel channels. Require 0 < kD <= inDepth + 2*padD; got (kD=" + kD + ", inDepth=" + inDepth + ", padD=" + padD + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputChannels, convolutionMode)); @@ -153,7 +153,7 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern String str = String.format("%.2f", d); int truncated = (int) d; int sameSize = (int) Math.ceil(inHeight / ((double) stride[0])); - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, true) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + "\nCombination of kernel size, stride and padding are not valid for given input height, using ConvolutionMode.Strict\n" + "ConvolutionMode.Strict requires: output height = (input height - kernelSize + 2*padding)/stride + 1 in height dimension to be an integer. Got: (" + inHeight + " - " + kH + " + 2*" + padH + ")/" + sH + " + 1 = " + str + "\n" @@ -170,7 +170,7 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern String str = String.format("%.2f", d); int truncated = (int) d; int sameSize = (int) Math.ceil(inWidth / ((double) stride[1])); - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + "\nCombination of kernel size, stride and padding are not valid for given input width, using ConvolutionMode.Strict\n" + "ConvolutionMode.Strict requires: output width = (input width - kernelSize + 2*padding)/stride + 1 in width dimension to be an integer. Got: (" + inWidth + " - " + kW + " + 2*" + padW + ")/" + sW + " + 1 = " + str + "\n" @@ -187,7 +187,7 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern String str = String.format("%.2f", d); int truncated = (int) d; int sameSize = (int) Math.ceil(inDepth / ((double) stride[2])); - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + "\nCombination of kernel size, stride and padding are not valid for given input width, using ConvolutionMode.Strict\n" + "ConvolutionMode.Strict requires: output channels = (input channels - kernelSize + 2*padding)/stride + 1 in width dimension to be an integer. Got: (" + inDepth + " - " + kD + " + 2*" + padD + ")/" + sD + " + 1 = " + str + "\n" @@ -214,6 +214,75 @@ public static InputType getOutputTypeCnn3DLayers(InputType inputType, int[] kern } + public static InputType getOutputTypeCnn1DLayers(InputType inputType, int kH, int sH, int padH, + int dilation, ConvolutionMode convolutionMode, long outputDepth, + long layerIdx, String layerName, + Class layerClass) { + + if (convolutionMode == null) { + String name = layerName == null ? "(not named)" : layerName; + throw new DL4JInvalidConfigException("Invalid configuration: convolution mode is null for layer (idx=" + + layerIdx + ", name=" + name + ", type=" + layerClass.getName() + ")"); + } + + InputType.InputTypeRecurrent i = (InputType.InputTypeRecurrent) inputType; + + val inHeight = (int) i.getTimeSeriesLength(); + if(dilation != 1){ + kH = kH + (kH-1)*(dilation-1); + } + + if (sH <= 0) { + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, sH <= 0) + + " Invalid strides: strides must be > 0 (strideH = " + sH + ")" + + "\n" + getConfigErrorCommonLastLine1D(inputType, kH, sH, padH, outputDepth, + convolutionMode)); + } + + if (kH <= 0 || kH > inHeight + 2 * padH) { + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + + " Invalid input configuration for kernel height. Require 0 < kH <= inHeight + 2*padH; got (kH=" + + kH + ", inHeight=" + inHeight + ", padH=" + padH + ")\n" + getConfigErrorCommonLastLine1D( + inputType, kH, sH, padH, outputDepth, convolutionMode)); + } + + + //Strict mode: require exactly the right size... + if (convolutionMode == ConvolutionMode.Strict) { + if ((inHeight - kH + 2 * padH) % sH != 0) { + double d = (inHeight - kH + 2 * padH) / ((double) sH) + 1.0; + String str = String.format("%.2f", d); + int truncated = (int) d; + int sameSize = (int) Math.ceil(inHeight / ((double) sH)); + throw new DL4JInvalidConfigException(getConfigErrorCommonLine( + layerIdx, layerName, layerClass, true) + + "\nCombination of kernel size, stride and padding are not valid for given input height, " + + "using ConvolutionMode.Strict\n" + + "ConvolutionMode.Strict requires: output height = (input height - kernelSize + " + + "2*padding)/stride + 1 in height dimension to be an integer. Got: (" + + inHeight + " - " + kH + " + 2*" + padH + ")/" + sH + " + 1 = " + str + "\n" + + "See ConvolutionType enumeration Javadoc and \"Constraints on strides\" at " + + "http://cs231n.github.io/convolutional-networks/\n" + + "To truncate/crop the input, such that output height = floor(" + str + ") = " + + truncated + ", use ConvolutionType.Truncate.\n" + + "Alternatively use ConvolutionType.Same, which will use padding to give an " + + "output height of ceil(" + + inHeight + "/" + sH + ")=" + sameSize + "\n" + getConfigErrorCommonLastLine1D( + inputType, kH, sH, padH, outputDepth, convolutionMode)); + } + + } else if (convolutionMode == ConvolutionMode.Same) { + + int outH = (int) Math.ceil(inHeight / ((double) sH)); + + return InputType.recurrent(outputDepth, outH); + } + + int outH = (inHeight - kH + 2 * padH) / sH + 1; + return InputType.recurrent(outputDepth, outH); + } + + public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernelSize, int[] stride, int[] padding, int[] dilation, ConvolutionMode convolutionMode, long outputDepth, long layerIdx, String layerName, Class layerClass) { @@ -245,21 +314,21 @@ public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernel int sW = stride[1]; if (sH <= 0 || sW <= 0) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, sH <= 0) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, sH <= 0) + " Invalid strides: strides must be > 0 (strideH = " + sH + ", strideW = " + sW + ")" + "\n" + getConfigErrorCommonLastLine(inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); } if (kH <= 0 || kH > inHeight + 2 * padH) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, true) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + " Invalid input configuration for kernel height. Require 0 < kH <= inHeight + 2*padH; got (kH=" + kH + ", inHeight=" + inHeight + ", padH=" + padH + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); } if (kW <= 0 || kW > inWidth + 2 * padW) { - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + " Invalid input configuration for kernel width. Require 0 < kW <= inWidth + 2*padW; got (kW=" + kW + ", inWidth=" + inWidth + ", padW=" + padW + ")\n" + getConfigErrorCommonLastLine( inputType, kernelSize, stride, padding, outputDepth, convolutionMode)); @@ -272,7 +341,7 @@ public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernel String str = String.format("%.2f", d); int truncated = (int) d; int sameSize = (int) Math.ceil(inHeight / ((double) stride[0])); - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, true) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, true) + "\nCombination of kernel size, stride and padding are not valid for given input height, using ConvolutionMode.Strict\n" + "ConvolutionMode.Strict requires: output height = (input height - kernelSize + 2*padding)/stride + 1 in height dimension to be an integer. Got: (" + inHeight + " - " + kH + " + 2*" + padH + ")/" + sH + " + 1 = " + str + "\n" @@ -290,7 +359,7 @@ public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernel String str = String.format("%.2f", d); int truncated = (int) d; int sameSize = (int) Math.ceil(inWidth / ((double) stride[1])); - throw new DL4JInvalidConfigException(getConfigErrorCommonLine1(layerIdx, layerName, layerClass, false) + throw new DL4JInvalidConfigException(getConfigErrorCommonLine(layerIdx, layerName, layerClass, false) + "\nCombination of kernel size, stride and padding are not valid for given input width, using ConvolutionMode.Strict\n" + "ConvolutionMode.Strict requires: output width = (input width - kernelSize + 2*padding)/stride + 1 in width dimension to be an integer. Got: (" + inWidth + " - " + kW + " + 2*" + padW + ")/" + sW + " + 1 = " + str + "\n" @@ -303,18 +372,6 @@ public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernel } } else if (convolutionMode == ConvolutionMode.Same) { - //'Same' padding mode: - //outH = ceil(inHeight / strideH) decimal division - //outW = ceil(inWidth / strideW) decimal division - - //padHeightSum = ((outH - 1) * strideH + kH - inHeight) - //padTop = padHeightSum / 2 integer division - //padBottom = padHeghtSum - padTop - - //padWidthSum = ((outW - 1) * strideW + kW - inWidth) - //padLeft = padWidthSum / 2 integer division - //padRight = padWidthSum - padLeft - int outH = (int) Math.ceil(inHeight / ((double) stride[0])); int outW = (int) Math.ceil(inWidth / ((double) stride[1])); @@ -326,8 +383,8 @@ public static InputType getOutputTypeCnnLayers(InputType inputType, int[] kernel return InputType.convolutional(hOut, wOut, outputDepth); } - private static String getConfigErrorCommonLine1(long layerIdx, String layerName, Class layerClass, - boolean isHeight) { + private static String getConfigErrorCommonLine(long layerIdx, String layerName, Class layerClass, + boolean isHeight) { String name = layerName == null ? "(not named)" : layerName; String layerType = layerClass.getSimpleName(); @@ -335,6 +392,13 @@ private static String getConfigErrorCommonLine1(long layerIdx, String layerName, + (isHeight ? "height" : "width") + " dimension: "; } + private static String getConfigErrorCommonLastLine1D(InputType inputType, int kernelSize, int stride, + int padding, long outputDepth, ConvolutionMode convolutionMode) { + return "Input type = " + inputType + ", kernel = " + kernelSize + ", strides = " + stride + ", padding = " + + padding + ", layer size (output channels) = " + outputDepth + ", convolution mode = " + + convolutionMode; + } + private static String getConfigErrorCommonLastLine(InputType inputType, int[] kernelSize, int[] stride, int[] padding, long outputDepth, ConvolutionMode convolutionMode) { return "Input type = " + inputType + ", kernel = " + Arrays.toString(kernelSize) + ", strides = " diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected1D.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected1D.java new file mode 100644 index 000000000000..d14381c38b0e --- /dev/null +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected1D.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2015-2018 Skymind, Inc. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.deeplearning4j.nn.conf.layers; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.val; +import org.deeplearning4j.nn.conf.ConvolutionMode; +import org.deeplearning4j.nn.conf.InputPreProcessor; +import org.deeplearning4j.nn.conf.NeuralNetConfiguration; +import org.deeplearning4j.nn.conf.inputs.InputType; +import org.deeplearning4j.nn.conf.layers.samediff.SDLayerParams; +import org.deeplearning4j.nn.conf.layers.samediff.SameDiffLayer; +import org.deeplearning4j.nn.conf.layers.samediff.SameDiffLayerUtils; +import org.deeplearning4j.nn.params.ConvolutionParamInitializer; +import org.deeplearning4j.nn.weights.WeightInitUtil; +import org.deeplearning4j.util.Convolution1DUtils; +import org.nd4j.autodiff.samediff.SDIndex; +import org.nd4j.autodiff.samediff.SDVariable; +import org.nd4j.autodiff.samediff.SameDiff; +import org.nd4j.linalg.activations.Activation; +import org.nd4j.linalg.api.memory.MemoryWorkspace; +import org.nd4j.linalg.api.ndarray.INDArray; +import org.nd4j.linalg.factory.Nd4j; +import org.nd4j.shade.jackson.annotation.JsonIgnoreProperties; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"paramShapes"}) +/** + * SameDiff version of a 1D locally connected layer. + * + * + * @author Max Pumperla + */ +public class LocallyConnected1D extends SameDiffLayer { + + private static final List WEIGHT_KEYS = Collections.singletonList(ConvolutionParamInitializer.WEIGHT_KEY); + private static final List BIAS_KEYS = Collections.singletonList(ConvolutionParamInitializer.BIAS_KEY); + private static final List PARAM_KEYS = Arrays.asList( + ConvolutionParamInitializer.BIAS_KEY, + ConvolutionParamInitializer.WEIGHT_KEY); + + private long nIn; + private long nOut; + private Activation activation; + private int kernel; + private int stride; + private int padding; + private ConvolutionMode cm; + private int dilation; + private boolean hasBias; + private int inputSize; + private int outputSize; + private int featureDim; + + protected LocallyConnected1D(Builder builder) { + super(builder); + this.nIn = builder.nIn; + this.nOut = builder.nOut; + this.activation = builder.activation; + this.kernel = builder.kernel; + this.stride = builder.stride; + this.padding = builder.padding; + this.cm = builder.cm; + this.dilation = builder.dilation; + this.hasBias = builder.hasBias; + this.inputSize = builder.inputSize; + this.featureDim = kernel * (int) nIn; + } + + private LocallyConnected1D(){ + //No arg constructor for Jackson/JSON serialization + } + + public void computeOutputSize() { + int nIn = (int) getNIn(); + if (inputSize == 0) { + throw new IllegalArgumentException("Input size has to be set for Locally connected layers"); + } + int[] inputShape = new int[] {1, nIn, inputSize}; + INDArray dummyInputForShapeInference = Nd4j.ones(inputShape); + + if (cm == ConvolutionMode.Same) { + this.outputSize = Convolution1DUtils.getOutputSize( + dummyInputForShapeInference, kernel, stride, 0, cm, dilation); + this.padding = Convolution1DUtils.getSameModeTopLeftPadding(outputSize, inputSize, kernel, stride, dilation); + } else { + this.outputSize = Convolution1DUtils.getOutputSize( + dummyInputForShapeInference, kernel, stride, padding, cm, dilation); + } + } + + @Override + public InputType getOutputType(int layerIndex, InputType inputType) { + if (inputType == null || inputType.getType() != InputType.Type.RNN) { + throw new IllegalArgumentException("Provided input type for locally connected 1D layers has to be " + + "of CNN1D/RNN type, got: " + inputType); + } + // dynamically compute input size from input type + InputType.InputTypeRecurrent rnnType = (InputType.InputTypeRecurrent) inputType; + this.inputSize = (int) rnnType.getTimeSeriesLength(); + computeOutputSize(); + + return InputTypeUtil.getOutputTypeCnn1DLayers(inputType, kernel, stride, padding, 1, + cm, nOut, layerIndex, getLayerName(), LocallyConnected1D.class); + } + + @Override + public void setNIn(InputType inputType, boolean override) { + if (nIn <= 0 || override) { + InputType.InputTypeRecurrent c = (InputType.InputTypeRecurrent) inputType; + this.nIn = c.getSize(); + } + } + + @Override + public InputPreProcessor getPreProcessorForInputType(InputType inputType) { + return InputTypeUtil.getPreprocessorForInputTypeRnnLayers(inputType, getLayerName()); + } + + @Override + public void defineParameters(SDLayerParams params) { + params.clear(); + val weightsShape = new long[]{outputSize, featureDim, nOut}; + params.addWeightParam(ConvolutionParamInitializer.WEIGHT_KEY, weightsShape); + if(hasBias) { + val biasShape = new long[]{1, nOut}; + params.addBiasParam(ConvolutionParamInitializer.BIAS_KEY, biasShape); + } + } + + @Override + public void initializeParameters(Map params) { + try(MemoryWorkspace ws = Nd4j.getWorkspaceManager().scopeOutOfWorkspaces()) { + for (Map.Entry e : params.entrySet()) { + if (ConvolutionParamInitializer.BIAS_KEY.equals(e.getKey())) { + e.getValue().assign(0); + } else { + double fanIn = nIn * kernel; + double fanOut = nOut * kernel / ((double) stride); + WeightInitUtil.initWeights( + fanIn, fanOut, e.getValue().shape(), weightInit, null, 'c', e.getValue()); + } + } + } + } + + @Override + public SDVariable defineLayer(SameDiff sameDiff, SDVariable layerInput, Map paramTable) { + + SDVariable w = paramTable.get(ConvolutionParamInitializer.WEIGHT_KEY); // (outH, featureDim, nOut) + // System.out.println(Arrays.toString(w.getShape())); + + long[] inputShape = layerInput.getShape(); + long miniBatch = inputShape[0]; + int outH = outputSize; + int sH = stride; + int kH = kernel; + + SDVariable[] inputArray = new SDVariable[outH]; + for (int i = 0; i < outH; i++) { + SDVariable slice = layerInput.get( + SDIndex.all(), // miniBatch + SDIndex.all(), // nIn + SDIndex.interval(i * sH, i * sH + kH) // kernel + ); + inputArray[i] = sameDiff.reshape(slice, 1, miniBatch, featureDim); + } + SDVariable concatOutput = sameDiff.concat(0, inputArray); // (outH, miniBatch, featureDim) + sameDiff.exec(); + + System.out.println(Arrays.toString(concatOutput.getShape())); + + SDVariable mmulResult = sameDiff.mmul(concatOutput, w); // (outH, miniBatch, nOut) + System.out.println(Arrays.toString(mmulResult.getShape())); + + SDVariable result = sameDiff.permute(mmulResult,1, 2, 0); // (miniBatch, nOut, outH) + + SDVariable b = sameDiff.zero("bias", new long[] {1, nOut}); + if(hasBias){ + b = paramTable.get(ConvolutionParamInitializer.BIAS_KEY); + } + SDVariable biasAddedResult = sameDiff.biasAdd(result, b); + return activation.asSameDiff("out", sameDiff, biasAddedResult); + + } + + @Override + public void applyGlobalConfigToLayer(NeuralNetConfiguration.Builder globalConfig) { + if (activation == null) { + activation = SameDiffLayerUtils.fromIActivation(globalConfig.getActivationFn()); + } + if (cm == null) { + cm = globalConfig.getConvolutionMode(); + } + } + + public static class Builder extends SameDiffLayer.Builder { + + private int nIn; + private int nOut; + private Activation activation = Activation.TANH; + private int kernel = 2; + private int stride = 1; + private int padding = 0; + private int dilation = 1; + private int inputSize; + private ConvolutionMode cm = ConvolutionMode.Same; + private boolean hasBias = false; + + public Builder nIn(int nIn) { + this.nIn = nIn; + return this; + } + + public Builder nOut(int nOut) { + this.nOut = nOut; + return this; + } + + + public Builder activation(Activation activation) { + this.activation = activation; + return this; + } + + public Builder kernelSize(int k) { + this.kernel = k; + return this; + } + + public Builder stride(int s) { + this.stride = s; + return this; + } + + public Builder padding(int p) { + this.padding = p; + return this; + } + + public Builder convolutionMode(ConvolutionMode cm) { + this.cm = cm; + return this; + } + + public Builder dilation(int d) { + this.dilation = d; + return this; + } + + public Builder hasBias(boolean hasBias){ + this.hasBias = hasBias; + return this; + } + + /** + * Set input filter size for this locally connected 1D layer + * + * @param inputSize height of the input filters + * @return Builder + */ + public Builder setInputSize(int inputSize){ ; + this.inputSize = inputSize; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public LocallyConnected1D build() { + Convolution1DUtils.validateConvolutionModePadding(cm, padding); + Convolution1DUtils.validateCnn1DKernelStridePadding(kernel, stride, padding); + return new LocallyConnected1D(this); + } + } +} diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected2D.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected2D.java index 5dc65a9802ea..1f63c8ab2182 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected2D.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/LocallyConnected2D.java @@ -68,7 +68,6 @@ protected LocallyConnected2D(Builder builder) { this.dilation = builder.dilation; this.hasBias = builder.hasBias; this.inputSize = builder.inputSize; - computeOutputSize(); this.featureDim = kernel[0] * kernel[1] * (int) nIn; } @@ -98,7 +97,15 @@ public void computeOutputSize() { @Override public InputType getOutputType(int layerIndex, InputType inputType) { - InputType.InputTypeConvolutional c = (InputType.InputTypeConvolutional) inputType; + if (inputType == null || inputType.getType() != InputType.Type.CNN) { + throw new IllegalArgumentException("Provided input type for locally connected 2D layers has to be " + + "of CNN type, got: " + inputType); + } + // dynamically compute input size from input type + InputType.InputTypeConvolutional cnnType = (InputType.InputTypeConvolutional) inputType; + this.inputSize = new int[] { (int) cnnType.getHeight(), (int) cnnType.getWidth()}; + computeOutputSize(); + return InputTypeUtil.getOutputTypeCnnLayers(inputType, kernel, stride, padding, new int[]{1, 1}, cm, nOut, layerIndex, getLayerName(), LocallyConnected2D.class); } @@ -160,24 +167,28 @@ public SDVariable defineLayer(SameDiff sameDiff, SDVariable layerInput, Map { private int[] dilation = new int[]{1, 1}; private int[] inputSize; private ConvolutionMode cm = ConvolutionMode.Same; - private boolean hasBias = true; + private boolean hasBias = false; public Builder nIn(int nIn) { this.nIn = nIn; diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/Convolution1DUtils.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/Convolution1DUtils.java new file mode 100644 index 000000000000..ee9fd65fed1e --- /dev/null +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/Convolution1DUtils.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2015-2018 Skymind, Inc. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.deeplearning4j.util; + + +import lombok.val; +import org.deeplearning4j.exception.DL4JInvalidConfigException; +import org.deeplearning4j.exception.DL4JInvalidInputException; +import org.deeplearning4j.nn.conf.ConvolutionMode; +import org.deeplearning4j.nn.conf.NeuralNetConfiguration; +import org.deeplearning4j.nn.conf.inputs.InputType; +import org.deeplearning4j.nn.workspace.ArrayType; +import org.deeplearning4j.nn.workspace.LayerWorkspaceMgr; +import org.nd4j.base.Preconditions; +import org.nd4j.linalg.api.ndarray.INDArray; +import org.nd4j.linalg.api.ops.impl.broadcast.BroadcastCopyOp; +import org.nd4j.linalg.api.shape.Shape; +import org.nd4j.linalg.factory.Nd4j; + +import java.util.Arrays; + +/** + * Shape utilities for 1D convolution layers + * + * @author Max Pumperla + */ +public class Convolution1DUtils { + + private static final int ONE = 1; + + + private Convolution1DUtils() { + } + + public static int getOutputSize(INDArray inputData, int kernel, int strides, int padding, + ConvolutionMode convolutionMode) { + return getOutputSize(inputData, kernel, strides, padding, convolutionMode, ONE); + } + + /** + * Get the output size (height) for the given input data and CNN1D configuration + * + * @param inputData Input data + * @param kernel Kernel size + * @param strides Stride + * @param padding Padding + * @param convolutionMode Convolution mode (Same, Strict, Truncate) + * @param dilation Kernel dilation + * @return Output size (width) + */ + public static int getOutputSize(INDArray inputData, int kernel, int strides, int padding, + ConvolutionMode convolutionMode, int dilation) { + // FIXME: int cast + int inH = (int) inputData.size(2); + int eKernel = effectiveKernelSize(kernel, dilation); + boolean atrous = (eKernel == kernel); + validateShapes(inputData, eKernel, strides, padding, convolutionMode, dilation, inH, atrous); + + if (convolutionMode == ConvolutionMode.Same) { + int outH = (int) Math.ceil(inH / ((double) strides)); + return outH; + } + + int outH = (inH - eKernel + 2 * padding) / strides + 1; + return outH; + } + + public static void validateShapes(INDArray inputData, int eKernel, int strides, int padding, + ConvolutionMode convolutionMode, int dilation, int inShape, + boolean atrous) { + + int inH = inShape; + + if (convolutionMode != ConvolutionMode.Same && (eKernel <= 0 || eKernel > inH + 2 * padding)) { + StringBuilder sb = new StringBuilder(); + sb.append("Invalid input data or configuration: "); + if (atrous) sb.append("effective "); + sb.append("kernel height and input height must satisfy 0 < "); + if (atrous) sb.append("effective "); + sb.append("kernel height <= input height + 2 * padding height. \nGot "); + if (atrous) sb.append("effective "); + sb.append("kernel height = ").append(eKernel).append(", input height = ").append(inH) + .append(" and padding height = ").append(padding).append(" which do not satisfy 0 < ") + .append(eKernel).append(" <= ").append(inH + 2 * padding) + .append(getCommonErrorMsg(inputData, eKernel, strides, padding, dilation)); + + throw new DL4JInvalidInputException(sb.toString()); + } + + + if (convolutionMode == ConvolutionMode.Strict) { + if ((inH - eKernel + 2 * padding) % strides != 0) { + double d = (inH - eKernel + 2 * padding) / ((double) strides) + 1.0; + String str = String.format("%.2f", d); + int truncated = (int) d; + int sameSize = (int) Math.ceil(inH / ((double) strides)); + + StringBuilder sb = new StringBuilder(); + sb.append("Invalid input data or configuration: Combination of kernel size, " + + "stride and padding are not " + + "valid for given input height, using ConvolutionMode.Strict\n") + .append("ConvolutionMode.Strict requires: output height = (input height - kernelSize + " + + "2*padding)/stride + 1 to be an integer. Got: (") + .append(inH).append(" - ").append(eKernel).append(" + 2*").append(padding).append(")/") + .append(strides).append(" + 1 = ") + .append(str).append("\n").append("See \"Constraints on strides\" at http://cs231n.github." + + "io/convolutional-networks/ and ConvolutionType enumeration Javadoc.\n") + .append("To truncate/crop the input, such that output height = floor(") + .append(str).append(") = ") + .append(truncated).append(", use ConvolutionType.Truncate.\n") + .append("Alternatively use ConvolutionType.Same, which will use padding to give an " + + "output height of ceil(") + .append(inH).append("/").append(strides).append(")=").append(sameSize) + .append(getCommonErrorMsg(inputData, eKernel, strides, padding, dilation)); + + throw new DL4JInvalidConfigException(sb.toString()); + } + } + + } + + public static int effectiveKernelSize(int kernel, int dilation) { + //Determine the effective kernel size, accounting for dilation + //http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html#dilated-convolutions + if (dilation == 1) { + return kernel; + } else { + return kernel + (kernel - 1) * (dilation - 1); + } + } + + private static String getCommonErrorMsg(INDArray inputData, int kernel, int strides, int padding, int dilation) { + String s = "\nInput size: [numExamples,inputDepth,inputHeight,inputWidth]=" + Arrays.toString(inputData.shape()) + + ", inputKernel=" + kernel; + if (dilation != 1) { + int effectiveKernel = effectiveKernelSize(kernel, dilation); + s += ", effectiveKernelGivenDilation=" + effectiveKernel; + } + return s + ", stride=" + strides + ", padding=" + padding + ", dilation=" + dilation; + } + + + /** + * Check that the convolution mode is consistent with the padding specification + */ + public static void validateConvolutionModePadding(ConvolutionMode mode, int padding) { + if (mode == ConvolutionMode.Same) { + boolean nullPadding = true; + if (padding != 0) nullPadding = false; + if (!nullPadding) + throw new IllegalArgumentException("Padding cannot be used when using the `same' convolution mode"); + + } + } + + /** + * Get top padding for same mode only. + * + * @param outSize Output size (length 2 array, height dimension first) + * @param inSize Input size (length 2 array, height dimension first) + * @param kernel Kernel size (length 2 array, height dimension first) + * @param strides Strides (length 2 array, height dimension first) + * @param dilation Dilation (length 2 array, height dimension first) + * @return Top left padding (length 2 array, height dimension first) + */ + public static int getSameModeTopLeftPadding(int outSize, int inSize, int kernel, int strides, int dilation) { + int eKernel = effectiveKernelSize(kernel, dilation); + //Note that padBottom is 1 bigger than this if bracketed term is not divisible by 2 + int outPad = ((outSize - 1) * strides + eKernel - inSize) / 2; + Preconditions.checkState(outPad >= 0, "Invalid padding values calculated: %s - " + + "layer configuration is invalid? Input size %s, output size %s, kernel %s, " + + "strides %s, dilation %s", outPad, inSize, outSize, kernel, strides, dilation); + return outPad; + } + + /** + * Perform validation on the CNN layer kernel/stride/padding. Expect int, with values > 0 for kernel size and + * stride, and values >= 0 for padding. + * + * @param kernel Kernel size to check + * @param stride Stride to check + * @param padding Padding to check + */ + public static void validateCnn1DKernelStridePadding(int kernel, int stride, int padding) { + + if (kernel <= 0) { + throw new IllegalStateException("Invalid kernel size: value must be positive (> 0). Got: " + kernel); + } + if (stride <= 0) { + throw new IllegalStateException("Invalid kernel size: value must be positive (> 0). Got: " + stride); + + } + if (padding < 0) { + throw new IllegalStateException("Invalid kernel size: value must be positive (> 0). Got: " + padding); + } + } + + +} diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ConvolutionUtils.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ConvolutionUtils.java index a7061d2bd23c..0ca2d90bbe4c 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ConvolutionUtils.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ConvolutionUtils.java @@ -393,7 +393,6 @@ public static void validateConvolutionModePadding(ConvolutionMode mode, int[] pa } } - /** * Perform validation on the CNN layer kernel/stride/padding. Expect 2d int[], with values > 0 for kernel size and * stride, and values >= 0 for padding.