diff --git a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/layers/convolution/SubsamplingLayerTest.java b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/layers/convolution/SubsamplingLayerTest.java index 69f8c22db..e0e556f39 100644 --- a/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/layers/convolution/SubsamplingLayerTest.java +++ b/deeplearning4j/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/layers/convolution/SubsamplingLayerTest.java @@ -155,7 +155,7 @@ public class SubsamplingLayerTest extends BaseDL4JTest { } - @Test(expected = IllegalStateException.class) + @Test(expected = UnsupportedOperationException.class) public void testSubSampleLayerSumBackprop() throws Exception { Layer layer = getSubsamplingLayer(SubsamplingLayer.PoolingType.SUM); INDArray input = getData(); diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SpaceToDepthLayer.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SpaceToDepthLayer.java index aeca265f8..44f8bb666 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SpaceToDepthLayer.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SpaceToDepthLayer.java @@ -92,7 +92,6 @@ public class SpaceToDepthLayer extends NoParamLayer { @Override public LayerMemoryReport getMemoryReport(InputType inputType) { - InputType.InputTypeConvolutional c = (InputType.InputTypeConvolutional) inputType; InputType.InputTypeConvolutional outputType = (InputType.InputTypeConvolutional) getOutputType(-1, inputType); return new LayerMemoryReport.Builder(layerName, SpaceToDepthLayer.class, inputType, outputType) diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SubsamplingLayer.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SubsamplingLayer.java index 9eff7a91a..b2e4df6b8 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SubsamplingLayer.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/SubsamplingLayer.java @@ -57,6 +57,12 @@ public class SubsamplingLayer extends NoParamLayer { protected int pnorm; protected double eps; protected boolean cudnnAllowFallback = true; + /* + Default here for JSON deserialization of 1.0.0-beta4 and earlier models. New models default to false via builder. + This impacts average pooling only - whether the divisor should include or exclude padding along image edges. + DL4J originally included padding in the count, versions after 1.0.0-beta4 will exclude it by default. + */ + protected boolean avgPoolIncludePadInDivisor = true; public enum PoolingType { MAX, AVG, SUM, PNORM; @@ -95,6 +101,7 @@ public class SubsamplingLayer extends NoParamLayer { this.pnorm = builder.pnorm; this.eps = builder.eps; this.cudnnAllowFallback = builder.cudnnAllowFallback; + this.avgPoolIncludePadInDivisor = builder.avgPoolIncludePadInDivisor; } @Override @@ -376,6 +383,7 @@ public class SubsamplingLayer extends NoParamLayer { * Whether fallback to non-CuDNN implementation should be used */ protected boolean cudnnAllowFallback = true; + protected boolean avgPoolIncludePadInDivisor = false; protected BaseSubsamplingBuilder(PoolingType poolingType, int[] kernelSize, int[] stride) { this.setPoolingType(poolingType.toPoolingType()); @@ -482,6 +490,29 @@ public class SubsamplingLayer extends NoParamLayer { this.cudnnAllowFallback = allowFallback; return (T) this; } + + /** + * When doing average pooling, should the padding values be included in the divisor or not?
+ * Not applicable for max and p-norm pooling.
+ * Users should not usually set this - instead, leave it as the default (false). It is included mainly for backward + * compatibility of older models
+ * Consider the following 2x2 segment along the right side of the image:
+ *
+         * [A, P]
+         * [B, P]
+         * 
+ * Where A and B are actual values, and P is padding (0).
+ * With avgPoolIncludePadInDivisor = true, we have: out = (A+B+0+0)/4
+ * With avgPoolIncludePadInDivisor = false, we have: out = (A+B+0+0)/2
+ *
+ * Earlier versions of DL4J originally included padding in the count, newer versions exclude it.
+ * + * @param avgPoolIncludePadInDivisor Whether the divisor should include or exclude padding for average pooling + */ + public T avgPoolIncludePadInDivisor(boolean avgPoolIncludePadInDivisor){ + this.avgPoolIncludePadInDivisor = avgPoolIncludePadInDivisor; + return (T) this; + } } } diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/samediff/AbstractSameDiffLayer.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/samediff/AbstractSameDiffLayer.java index 74d5f450e..6cf4ae810 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/samediff/AbstractSameDiffLayer.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/conf/layers/samediff/AbstractSameDiffLayer.java @@ -35,6 +35,7 @@ import org.deeplearning4j.optimize.api.TrainingListener; import org.deeplearning4j.util.NetworkUtils; import org.nd4j.linalg.api.buffer.DataType; import org.nd4j.linalg.api.ndarray.INDArray; +import org.nd4j.linalg.factory.Nd4j; import org.nd4j.linalg.learning.config.IUpdater; import org.nd4j.linalg.learning.regularization.L1Regularization; import org.nd4j.linalg.learning.regularization.L2Regularization; @@ -205,6 +206,22 @@ public abstract class AbstractSameDiffLayer extends Layer { applyGlobalConfigToLayer(b); } + /** + * This method generates an "all ones" mask array for use in the SameDiff model when none is provided. + * @param input Input to the layer + * @return A mask array - should be same datatype as the input (usually) + */ + public INDArray onesMaskForInput(INDArray input){ + if(input.rank() == 2){ + return Nd4j.ones(input.dataType(), input.size(0), 1); + } else if(input.rank() == 3){ + return Nd4j.ones(input.dataType(), input.size(0), input.size(2)); //mask: [mb, length] vs. input [mb, nIn, length] + } else { + throw new IllegalStateException("When using masking with rank 4+ inputs, the onesMaskForInput method must be implemented, " + + "in order to determine the correct mask shape for this layer"); + } + } + @Getter @Setter public static abstract class Builder> extends Layer.Builder { diff --git a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/layers/convolution/SpaceToDepth.java b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/layers/convolution/SpaceToDepth.java index 5516738fc..b726ea87c 100644 --- a/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/layers/convolution/SpaceToDepth.java +++ b/deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/layers/convolution/SpaceToDepth.java @@ -109,7 +109,7 @@ public class SpaceToDepth extends AbstractLayer ret = null; @@ -173,116 +160,42 @@ public class SubsamplingLayer extends AbstractLayer(retGradient, outEpsilon); + return new Pair<>(retGradient, epsAtInput); } private static double minValue(){ @@ -326,7 +239,8 @@ public class SubsamplingLayer extends AbstractLayer { phMap.put(INPUT_KEY, input); if(maskArray != null){ phMap.put(MASK_KEY, maskArray); + } else { + phMap.put(MASK_KEY, layerConf().onesMaskForInput(input)); } for(String s : paramTable.keySet() ) { @@ -139,6 +141,8 @@ public class SameDiffLayer extends AbstractLayer { phMap.put(fn.getGradPlaceholderName(), epsilon); if(maskArray != null){ phMap.put(MASK_KEY, maskArray); + } else { + phMap.put(MASK_KEY, layerConf().onesMaskForInput(input)); } List requiredGrads = new ArrayList<>(paramTable.size() + 1);