Repository: deeplearning4j/deeplearning4j
Issue: Error using TransferLearningHelper with TinyYOLO

Hi

By calling the following code:

TinyYOLO model = new TinyYOLO(nClasses, seed);
ComputationGraph computationGraph = (ComputationGraph) model.initPretrained();
TransferLearningHelper transferLearningHelper = new TransferLearningHelper(computationGraph, "conv2d_9");

We get the following exception stacktrace:

Exception in thread "main" org.nd4j.linalg.exception.ND4JIllegalStateException: Invalid shape: Requested INDArray shape [1, 0] contains dimension size values < 1 (all dimensions must be 1 or more)
	at org.nd4j.linalg.factory.Nd4j.checkShapeValues(Nd4j.java:5122)
	at org.nd4j.linalg.factory.Nd4j.create(Nd4j.java:5112)
	at org.nd4j.linalg.factory.Nd4j.create(Nd4j.java:5065)
	at org.nd4j.linalg.factory.Nd4j.create(Nd4j.java:4170)
	at org.deeplearning4j.nn.graph.ComputationGraph.init(ComputationGraph.java:452)
	at org.deeplearning4j.nn.graph.ComputationGraph.init(ComputationGraph.java:377)
	at org.deeplearning4j.nn.transferlearning.TransferLearning$GraphBuilder.build(TransferLearning.java:755)
	at org.deeplearning4j.nn.transferlearning.TransferLearningHelper.initHelperGraph(TransferLearningHelper.java:252)
	at org.deeplearning4j.nn.transferlearning.TransferLearningHelper.<init>(TransferLearningHelper.java:56)
	at nlb.ai.deeplearning.dl4j.image.objectdetection.PersonsDetection.main(PersonsDetection.java:39)

It works perfectly well with VGG16.

* Deeplearning4j version 0.9.2-SANPSHOT
* windows 10
* neither CUDA nor NVIDIA are used



Changed files:
deeplearning4j-core/src/test/java/org/deeplearning4j/nn/multilayer/MultiLayerTest.java
deeplearning4j-zoo/src/test/java/org/deeplearning4j/zoo/TestInstantiation.java
deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/graph/ComputationGraph.java
deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/gradient/DefaultGradient.java
deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/multilayer/MultiLayerNetwork.java
deeplearning4j-core/src/test/java/org/deeplearning4j/nn/graph/TestComputationGraphNetwork.java
deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.java
deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/updater/BaseMultiLayerUpdater.java

diff --git a/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/graph/TestComputationGraphNetwork.java b/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/graph/TestComputationGraphNetwork.java
index 76098e52ca..8c776ce99c 100644
--- a/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/graph/TestComputationGraphNetwork.java
+++ b/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/graph/TestComputationGraphNetwork.java
@@ -6,8 +6,10 @@ import org.datavec.api.records.reader.RecordReader;
 import org.datavec.api.records.reader.impl.csv.CSVRecordReader;
 import org.datavec.api.split.FileSplit;
 import org.deeplearning4j.BaseDL4JTest;
+import org.deeplearning4j.TestUtils;
 import org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator;
 import org.deeplearning4j.datasets.iterator.impl.IrisDataSetIterator;
+import org.deeplearning4j.datasets.iterator.impl.MnistDataSetIterator;
 import org.deeplearning4j.eval.Evaluation;
 import org.deeplearning4j.exception.DL4JException;
 import org.deeplearning4j.nn.api.OptimizationAlgorithm;
@@ -1340,4 +1342,32 @@ public class TestComputationGraphNetwork extends BaseDL4JTest {
         assertEquals(13, net.layerSize("3"));
     }
 
+    @Test
+    public void testZeroParamNet() throws Exception {
+
+        ComputationGraphConfiguration conf = new NeuralNetConfiguration.Builder()
+                .graphBuilder()
+                .addInputs("in")
+                .layer("0", new SubsamplingLayer.Builder().kernelSize(2,2).stride(2,2).build(), "in")
+                .layer("1", new LossLayer.Builder().activation(Activation.SIGMOID).lossFunction(LossFunctions.LossFunction.MSE).build(), "0")
+                .setOutputs("1")
+                .setInputTypes(InputType.convolutionalFlat(28,28,1))
+                .build();
+
+        ComputationGraph net = new ComputationGraph(conf);
+        net.init();
+
+        DataSet ds = new MnistDataSetIterator(16, true, 12345).next();
+
+        INDArray out = net.outputSingle(ds.getFeatures());
+
+        INDArray labelTemp = Nd4j.create(out.shape());
+        ds.setLabels(labelTemp);
+
+        net.fit(ds);
+
+        ComputationGraph net2 = TestUtils.testModelSerialization(net);
+        INDArray out2 = net2.outputSingle(ds.getFeatures());
+        assertEquals(out, out2);
+    }
 }
diff --git a/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/multilayer/MultiLayerTest.java b/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/multilayer/MultiLayerTest.java
index 6b3b87d013..af21fb6c08 100644
--- a/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/multilayer/MultiLayerTest.java
+++ b/deeplearning4j-core/src/test/java/org/deeplearning4j/nn/multilayer/MultiLayerTest.java
@@ -1245,4 +1245,32 @@ public class MultiLayerTest extends BaseDL4JTest {
         assertEquals(30, net.layerSize(2));
         assertEquals(13, net.layerSize(3));
     }
+
+
+    @Test
+    public void testZeroParamNet() throws Exception {
+
+        MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
+                .list()
+                .layer(new SubsamplingLayer.Builder().kernelSize(2,2).stride(2,2).build())
+                .layer(new LossLayer.Builder().activation(Activation.SIGMOID).lossFunction(LossFunctions.LossFunction.MSE).build())
+                .setInputType(InputType.convolutionalFlat(28,28,1))
+                .build();
+
+        MultiLayerNetwork net = new MultiLayerNetwork(conf);
+        net.init();
+
+        DataSet ds = new MnistDataSetIterator(16, true, 12345).next();
+
+        INDArray out = net.output(ds.getFeatures());
+
+        INDArray labelTemp = Nd4j.create(out.shape());
+        ds.setLabels(labelTemp);
+
+        net.fit(ds);
+
+        MultiLayerNetwork net2 = TestUtils.testModelSerialization(net);
+        INDArray out2 = net2.output(ds.getFeatures());
+        assertEquals(out, out2);
+    }
 }
diff --git a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/gradient/DefaultGradient.java b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/gradient/DefaultGradient.java
index d09015682b..dea4e3e4d3 100644
--- a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/gradient/DefaultGradient.java
+++ b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/gradient/DefaultGradient.java
@@ -93,7 +93,7 @@ public class DefaultGradient implements Gradient {
                 }
             }
             flattenedGradient = Nd4j.toFlattened(DEFAULT_FLATTENING_ORDER, toFlatten);
-        } else {
+        } else if( !gradients.values().isEmpty() ){ //Edge case: can be empty for nets with 0 params
             //Standard case: flatten all to f order
             flattenedGradient = Nd4j.toFlattened(DEFAULT_FLATTENING_ORDER, gradients.values());
         }
diff --git a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/graph/ComputationGraph.java b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/graph/ComputationGraph.java
index 59399df257..db983b45b1 100755
--- a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/graph/ComputationGraph.java
+++ b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/graph/ComputationGraph.java
@@ -449,9 +449,12 @@ public class ComputationGraph implements Serializable, Model, NeuralNetwork {
                 flattenedParams = parameters;
 
             initializeParams = false;
-        } else {
+        } else if(numParams > 0){
             flattenedParams = Nd4j.create(1, numParams);
             initializeParams = true;
+        } else {
+            flattenedParams = null;
+            initializeParams = false;
         }
 
         //Set RNG seed, for repeatability between initializations when set
@@ -632,7 +635,10 @@ public class ComputationGraph implements Serializable, Model, NeuralNetwork {
                 numParams += numParamsForVertex[i];
                 i++;
             }
-            flattenedGradients = Nd4j.create(1, numParams);
+
+            if(numParams > 0) {
+                flattenedGradients = Nd4j.create(1, numParams);
+            }
 
             //Given the topological ordering: work out the subset of the gradient array used for each layer, and set it
             int paramOffsetSoFar = 0;
@@ -1138,6 +1144,10 @@ public class ComputationGraph implements Serializable, Model, NeuralNetwork {
      * @param labelMaskArrays   Mas arrays for the labels/outputs. Typically used for RNN training. May be null.
      */
     public void fit(INDArray[] inputs, INDArray[] labels, INDArray[] featureMaskArrays, INDArray[] labelMaskArrays) {
+        if (numParams() == 0) {
+            return; //Edge case: net with no params: fitting is a no-op
+        }
+
         if (flattenedGradients == null) {
             initGradientsView();
         }
diff --git a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/multilayer/MultiLayerNetwork.java b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/multilayer/MultiLayerNetwork.java
index b220483e91..ce19832f2a 100755
--- a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/multilayer/MultiLayerNetwork.java
+++ b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/multilayer/MultiLayerNetwork.java
@@ -604,9 +604,13 @@ public class MultiLayerNetwork implements Serializable, Classifier, Layer, Neura
                     flattenedParams = parameters;
 
                 initializeParams = false;
-            } else {
+            } else if(paramLength > 0){
                 flattenedParams = Nd4j.create(1, paramLength);
                 initializeParams = true;
+            } else {
+                //Edge case: 0 params in network
+                flattenedParams = null;
+                initializeParams = false;
             }
 
             //Set RNG seed, for repeatability between initializations when set
@@ -693,7 +697,9 @@ public class MultiLayerNetwork implements Serializable, Classifier, Layer, Neura
                 paramLength += nParamsPerLayer[i];
             }
 
-            flattenedGradients = Nd4j.zeros(new int[] {1, paramLength}, 'f'); //No need to initialize, as each layer will do it each iteration anyway
+            if(paramLength > 0) {
+                flattenedGradients = Nd4j.zeros(new int[]{1, paramLength}, 'f'); //No need to initialize, as each layer will do it each iteration anyway
+            }
 
             int backpropParamsSoFar = 0;
             for (int i = 0; i < layers.length; i++) {
@@ -1861,6 +1867,10 @@ public class MultiLayerNetwork implements Serializable, Classifier, Layer, Neura
      * @param labelsMask The mask array for the labels (used for variable length time series, etc). May be null.
      */
     public void fit(INDArray features, INDArray labels, INDArray featuresMask, INDArray labelsMask) {
+        if(numParams() == 0){
+            //No op: can't fit a network with 0 parameters
+            return;
+        }
 
         setInput(features);
         setLabels(labels);
diff --git a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/updater/BaseMultiLayerUpdater.java b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/updater/BaseMultiLayerUpdater.java
index ba08beb97f..da46b30e77 100644
--- a/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/updater/BaseMultiLayerUpdater.java
+++ b/deeplearning4j-nn/src/main/java/org/deeplearning4j/nn/updater/BaseMultiLayerUpdater.java
@@ -299,7 +299,11 @@ public abstract class BaseMultiLayerUpdater<T extends Model> implements Updater
                 gradient.gradient().divi(batchSize);
             } else {
                 //Standard case
-                getFlattenedGradientsView().divi(batchSize);
+                INDArray grad = getFlattenedGradientsView();
+                if(grad != null) {
+                    //May be null for nets with no parameters
+                    grad.divi(batchSize);
+                }
             }
         }
     }
diff --git a/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.java b/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.java
index e0644a9f1b..70694cc547 100644
--- a/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.java
+++ b/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.java
@@ -125,12 +125,18 @@ public class ModelSerializer {
         ZipEntry coefficients = new ZipEntry("coefficients.bin");
         zipfile.putNextEntry(coefficients);
         DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(zipfile));
-        try {
-            Nd4j.write(model.params(), dos);
-        } finally {
-            dos.flush();
-            if (!saveUpdater)
-                dos.close();
+        INDArray params = model.params();
+        if(params != null) {
+            try {
+                Nd4j.write(model.params(), dos);
+            } finally {
+                dos.flush();
+                if (!saveUpdater)
+                    dos.close();
+            }
+        } else {
+            ZipEntry noParamsMarker = new ZipEntry("noParams.marker");
+            zipfile.putNextEntry(noParamsMarker);
         }
 
         if (saveUpdater) {
@@ -225,13 +231,18 @@ public class ModelSerializer {
 
 
         ZipEntry coefficients = zipFile.getEntry("coefficients.bin");
-        if (coefficients != null) {
-            InputStream stream = zipFile.getInputStream(coefficients);
-            DataInputStream dis = new DataInputStream(new BufferedInputStream(stream));
-            params = Nd4j.read(dis);
+        if (coefficients != null ) {
+            if(coefficients.getSize() > 0) {
+                InputStream stream = zipFile.getInputStream(coefficients);
+                DataInputStream dis = new DataInputStream(new BufferedInputStream(stream));
+                params = Nd4j.read(dis);
 
-            dis.close();
-            gotCoefficients = true;
+                dis.close();
+                gotCoefficients = true;
+            } else {
+                ZipEntry noParamsMarker = zipFile.getEntry("noParams.marker");
+                gotCoefficients = (noParamsMarker != null);
+            }
         }
 
         if (loadUpdater) {
@@ -470,12 +481,17 @@ public class ModelSerializer {
 
         ZipEntry coefficients = zipFile.getEntry("coefficients.bin");
         if (coefficients != null) {
-            InputStream stream = zipFile.getInputStream(coefficients);
-            DataInputStream dis = new DataInputStream(new BufferedInputStream(stream));
-            params = Nd4j.read(dis);
+            if(coefficients.getSize() > 0) {
+                InputStream stream = zipFile.getInputStream(coefficients);
+                DataInputStream dis = new DataInputStream(new BufferedInputStream(stream));
+                params = Nd4j.read(dis);
 
-            dis.close();
-            gotCoefficients = true;
+                dis.close();
+                gotCoefficients = true;
+            } else {
+                ZipEntry noParamsMarker = zipFile.getEntry("noParams.marker");
+                gotCoefficients = (noParamsMarker != null);
+            }
         }
 
 
diff --git a/deeplearning4j-zoo/src/test/java/org/deeplearning4j/zoo/TestInstantiation.java b/deeplearning4j-zoo/src/test/java/org/deeplearning4j/zoo/TestInstantiation.java
index 66d0b54f95..10232887cd 100644
--- a/deeplearning4j-zoo/src/test/java/org/deeplearning4j/zoo/TestInstantiation.java
+++ b/deeplearning4j-zoo/src/test/java/org/deeplearning4j/zoo/TestInstantiation.java
@@ -5,6 +5,7 @@ import org.deeplearning4j.datasets.iterator.impl.BenchmarkDataSetIterator;
 import org.deeplearning4j.nn.api.Model;
 import org.deeplearning4j.nn.graph.ComputationGraph;
 import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
+import org.deeplearning4j.nn.transferlearning.TransferLearningHelper;
 import org.deeplearning4j.zoo.model.Darknet19;
 import org.deeplearning4j.zoo.model.GoogLeNet;
 import org.deeplearning4j.zoo.model.ResNet50;
@@ -139,4 +140,16 @@ public class TestInstantiation {
         assertArrayEquals(result[0].shape(), new int[] {1, 125, 13, 13});
     }
 
+
+    @Test
+    public void testYolo4635() throws Exception {
+        //https://github.com/deeplearning4j/deeplearning4j/issues/4635
+
+        int nClasses = 10;
+        int seed = 12345;
+        TinyYOLO model = new TinyYOLO(nClasses, seed);
+        ComputationGraph computationGraph = (ComputationGraph) model.initPretrained();
+        TransferLearningHelper transferLearningHelper = new TransferLearningHelper(computationGraph, "conv2d_9");
+    }
+
 }