Example #1
0
            /// <summary>
            ///
            /// </summary>
            /// <param name="input"></param>
            /// <param name="convolution_map_size"></param>
            /// <param name="device"></param>
            /// <param name="use_padding"></param>
            /// <param name="use_bias"></param>
            /// <param name="strides"></param>
            /// <param name="activation"></param>
            /// <param name="outputName"></param>
            /// <returns></returns>
            public static Function Convolution(
                Variable input,
                int[] convolution_map_size,
                DeviceDescriptor device,
                bool use_padding,
                bool use_bias,
                int[] strides,
                Func <Variable, string, Function> activation = null,
                string outputName = "")
            {
                var W = new Parameter(
                    NDShape.CreateNDShape(convolution_map_size),
                    DataType.Float,
                    CNTKLib.GlorotUniformInitializer(CNTKLib.DefaultParamInitScale, CNTKLib.SentinelValueForInferParamInitRank, CNTKLib.SentinelValueForInferParamInitRank, 1),
                    device,
                    outputName + "_W");
                //y = Wx+b
                Variable y = CNTKLib.Convolution(W, input, strides, new BoolVector(new bool[] { true }) /* sharing */, new BoolVector(new bool[] { use_padding }));

                //apply bias
                if (use_bias)
                {
                    var num_output_channels = convolution_map_size[convolution_map_size.Length - 1];
                    var b_shape             = Concat(Enumerable.Repeat(1, convolution_map_size.Length - 2).ToArray(), new int[] { num_output_channels });
                    var b = new Parameter(b_shape, 0.0f, device, outputName + "_b");
                    y = CNTKLib.Plus(y, b);
                }
                //apply activation
                if (activation != null)
                {
                    y = activation(y, outputName);
                }
                return(y);
            }
Example #2
0
        /// <summary>
        /// Implementation of : http://colah.github.io/posts/2015-08-Understanding-LSTMs/
        /// </summary>
        public LSTM LSTMNode(Variable input, int cellDims)
        {
            int outputDim = cellDims;

            Variable previousC = CNTKLib.PlaceholderVariable(NDShape.CreateNDShape(new int[] { cellDims }), DataType.Double, "previousC", new AxisVector(input.DynamicAxes.ToList()));  // placeholder for graph building
            Variable previousH = CNTKLib.PlaceholderVariable(NDShape.CreateNDShape(new int[] { outputDim }), DataType.Double, "previousH", new AxisVector(input.DynamicAxes.ToList())); // placeholder for graph building

            Variable forgetGate = DenseNode(CNTKLib.Plus(LinearNode(input, cellDims), LinearNode(previousH, cellDims)), cellDims, CNTKLib.Sigmoid);
            Variable inputGate  = DenseNode(CNTKLib.Plus(LinearNode(input, cellDims), LinearNode(previousH, cellDims)), cellDims, CNTKLib.Sigmoid);
            Variable partialC   = DenseNode(CNTKLib.Plus(LinearNode(input, cellDims), LinearNode(previousH, cellDims)), cellDims, CNTKLib.Sigmoid);

            Function filteredOldState = CNTKLib.ElementTimes(forgetGate, previousC); // element wise multiplication https://www.mathworks.com/help/matlab/ref/times.html
            Function filteredNewState = CNTKLib.ElementTimes(inputGate, partialC);
            Function c = CNTKLib.Plus(filteredOldState, filteredNewState);

            Function outputGate = DenseNode(CNTKLib.Plus(LinearNode(input, cellDims), LinearNode(previousH, cellDims)), outputDim, CNTKLib.Sigmoid);
            Function h          = CNTKLib.ElementTimes(outputGate, c);

            // replace placeholders by recursive variables
            h.ReplacePlaceholders(new Dictionary <Variable, Variable>
            {
                { previousH, CNTKLib.PastValue(h) },
                { previousC, CNTKLib.PastValue(c) },
            });

            return(new LSTM
            {
                H_output = h,
                C_cellstate = c
            });
        }
Example #3
0
        private Function Conv(int[] convMapSize, Variable x, DataType dType, DeviceDescriptor device, bool usePadding, bool useBias, string name, uint seed)
        {
            var shape = CNTK.NDShape.CreateNDShape(convMapSize);
            var W     = Weights(shape, dType, device, seed);

            //
            var strides  = new int[] { 1 };
            var sharing  = new bool[] { true };
            var aPadding = new bool[] { usePadding };
            //
            var result = CNTKLib.Convolution(W, x, strides, sharing, aPadding);

            if (useBias)
            {
                var intArray = convMapSize.Length == 4 ?
                               new int[] { 1, 1, NDShape.InferredDimension } :
                new int[] { 1, };

                var bShape = NDShape.CreateNDShape(intArray);

                var b = Bias(bShape, dType, device);

                result = CNTK.CNTKLib.Plus(result, b);
            }

            result = CNTK.CNTKLib.ReLU(result, name);

            return(result);
        }
        // Todo: move it to separate unit tests.
        public void NDArrayViewTest(DeviceDescriptor device)
        {
            Console.WriteLine("Test creating NDArrayView on devices.");

            var data       = new float[10];
            var shape      = new NDShape(1, 10);
            var n1         = new NDArrayView(shape, data, device);
            var n1Clone    = n1.DeepClone(device);
            var n1CloneCPU = n1.DeepClone(DeviceDescriptor.CPUDevice);

            Console.WriteLine("n1: on " + n1.Device.AsString() + ", Storage:" + n1.StorageFormat + ", Shape:" + n1.Shape.AsString());
            Console.WriteLine("n1Clone: on " + n1Clone.Device.AsString() + ", Storage:" + n1Clone.StorageFormat + ", Shape:" + n1Clone.Shape.AsString());
            Console.WriteLine("n1CloneCPU: on " + n1CloneCPU.Device.AsString() + ", Storage:" + n1CloneCPU.StorageFormat + ", Shape:" + n1CloneCPU.Shape.AsString());

            int[] dimensions = { 4, 5 };
            var   shape2     = NDShape.CreateNDShape(dimensions);

            float[] nonZeroValues = { 1, 5, 4, 2, 3, 9, 7, 8, 6 };
            int[]   rowIndices    = { 0, 2, 0, 1, 1, 3, 2, 2, 3 };
            int[]   colStarts     = { 0, 2, 4, 6, 7, 9 };
            var     s1            = new NDArrayView(shape2, colStarts, rowIndices, nonZeroValues, device, true);
            var     s1Clone       = s1.DeepClone(device);
            var     s1DenseCPU    = new NDArrayView(DataType.Float, StorageFormat.Dense, shape2, DeviceDescriptor.CPUDevice);

            s1DenseCPU.CopyFrom(s1);

            Console.WriteLine("s1: on " + s1.Device.AsString() + ", Storage:" + s1.StorageFormat + ", Shape:" + s1.Shape.AsString());
            Console.WriteLine("s1Clone: on " + s1Clone.Device.AsString() + ", Storage:" + s1Clone.StorageFormat + ", Shape:" + s1Clone.Shape.AsString());
            Console.WriteLine("s1DenseCPU: on " + s1DenseCPU.Device.AsString() + ", Storage:" + s1DenseCPU.StorageFormat + ", Shape:" + s1DenseCPU.Shape.AsString());
        }
Example #5
0
        /// <summary>
        /// create model by w,h,c,outputClassNum
        /// </summary>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <param name="c"></param>
        /// <param name="outputClassNum"></param>
        /// <param name="deviceName"></param>
        public FCN7(int w, int h, int c, int outputClassNum, string deviceName)
        {
            device = NP.CNTKHelper.GetDeviceByName(deviceName);
            //input output variable
            int[] inputDim  = new int[] { w, h, c };
            int[] outputDim = new int[] { outputClassNum };
            inputVariable  = Variable.InputVariable(NDShape.CreateNDShape(inputDim), DataType.Float, "inputVariable");
            outputVariable = Variable.InputVariable(NDShape.CreateNDShape(outputDim), DataType.Float, "labelVariable");
            //build model
            classifierOutput = CreateFullyChannelNetwork(inputVariable, c, outputClassNum);
            Function loss = CNTKLib.SquaredError(classifierOutput, outputVariable);
            Function pred = CNTKLib.ClassificationError(classifierOutput, outputVariable);
            //adam leaner
            ParameterVector parameterVector = new ParameterVector(classifierOutput.Parameters().ToList());
            TrainingParameterScheduleDouble learningRateSchedule = new TrainingParameterScheduleDouble(0.00178125, BatchSize);
            TrainingParameterScheduleDouble momentumRateSchedule = new TrainingParameterScheduleDouble(0.9, BatchSize);
            Learner leaner = CNTKLib.AdamLearner(parameterVector, learningRateSchedule, momentumRateSchedule, true);

            //构造leaner方法
            trainer = Trainer.CreateTrainer(classifierOutput, loss, pred, new List <Learner>()
            {
                leaner
            });
            //TrainingParameterScheduleDouble learningRatePerSample = new TrainingParameterScheduleDouble(0.00178125, BatchSize); //0.00178125
            //TrainingParameterScheduleDouble momentumTimeConstant = CNTKLib.MomentumAsTimeConstantSchedule(256);
            //IList<Learner> parameterLearners = new List<Learner>() { Learner.MomentumSGDLearner(classifierOutput.Parameters(), learningRatePerSample, momentumTimeConstant, true) };
            //trainer = Trainer.CreateTrainer(classifierOutput, loss, pred, parameterLearners);
        }
Example #6
0
        Function Discriminator(Function input, Func <CNTKDictionary> weightInit, CNTKDictionary biasInit,
                               DeviceDescriptor device, DataType dataType)
        {
            var discriminatorNetwork = input
                                       .Reshape(NDShape.CreateNDShape(new int[] { 28, 28, 1 }))

                                       .Conv2D((5, 5), 1, (2, 2), Padding.None, weightInit(), biasInit, device, dataType)
                                       .BatchNorm(BatchNorm.Spatial, device, dataType)
                                       .LeakyReLU(0.2)

                                       .Conv2D((5, 5), 64, (2, 2), Padding.None, weightInit(), biasInit, device, dataType)
                                       .BatchNorm(BatchNorm.Spatial, device, dataType)
                                       .LeakyReLU(0.2)

                                       .Dense(1024, weightInit(), biasInit, device, dataType)
                                       .BatchNorm(BatchNorm.Regular, device, dataType)
                                       .LeakyReLU(0.2)

                                       .Dense(1, weightInit(), biasInit, device, dataType)
                                       .Sigmoid();

            Trace.Write(Model.Summary(discriminatorNetwork));

            return(discriminatorNetwork);
        }
Example #7
0
        public Tensor categorical_crossentropy(Tensor target, Tensor output, bool from_logits = false)
        {
            // https://github.com/fchollet/keras/blob/f65a56fb65062c8d14d215c9f4b1015b97cc5bf3/keras/backend/cntk_backend.py#L1480

            var _output = In(output);
            var _target = In(target);

            if (from_logits)
            {
                var result = C.CrossEntropyWithSoftmax(_output, _target);
                // cntk's result shape is (batch, 1), while keras expect (batch, )
                CNTK.Function r = C.Reshape(result, NDShape.CreateNDShape(new int[] { }));
                return(Out(r));
            }
            else
            {
                // scale preds so that the class probas of each sample sum to 1
                var o     = C.ElementDivide(_output.function, C.ReduceSum(_output, Axis.EndStaticAxis()));
                var eps   = Constant.Scalar(epsilon(), DeviceDescriptor.CPUDevice);
                var omeps = Constant.Scalar(1.0 - epsilon(), DeviceDescriptor.CPUDevice);
                // avoid numerical instability with _EPSILON clipping
                o = C.Clip(o, eps, omeps);
                CNTK.Function r = C.Negate(C.ReduceSum(C.ElementTimes(_target, C.Log(_output)), Axis.EndStaticAxis()));
                return(Out(r));
            }
        }
Example #8
0
        private CNTK.Function _reshape_dummy_dim(CNTK.Function x, params int[] axis)
        {
            // https://github.com/fchollet/keras/blob/f65a56fb65062c8d14d215c9f4b1015b97cc5bf3/keras/backend/cntk_backend.py#L680

            List <int> shape = In(x.Output.Shape).ToList();


            var _axis = axis.Select(i => i < 0 ? (i + shape.Count) : i).ToArray();

            if (shape.Count(s => s == NDShape.InferredDimension) > 1)
            {
                var result = x;
                foreach (int index in _axis.Sorted().Reverse())
                {
                    result = C.Reshape(result, replacementShape: NDShape.CreateNDShape(new int[] { }),
                                       beginAxis: new Axis(index), endAxis: new Axis(index + 1));
                }
                return(result);
            }
            else
            {
                foreach (int index in _axis.Sorted().Reverse())
                {
                    shape.RemoveAt(index);
                }

                return(C.Reshape(x, NDShape.CreateNDShape(shape)));
            }
        }
Example #9
0
        public Function LinearNode(Variable input, int outputDim)
        {
            if (input.Shape.Rank != 1)
            {
                // un dense layer prend un vecteur seulement en entrée => on transforme l'entrée en vecteur
                int nbDims = 1;
                for (int i = 0; i < input.Shape.Dimensions.Count; i++)
                {
                    nbDims = nbDims * input.Shape.Dimensions[i];
                }

                var vector = CNTKLib.Reshape(input, NDShape.CreateNDShape(new[] { nbDims }));
                return(LinearNode(vector, outputDim));
            }

            List <int> weightDimensions = new List <int>();

            weightDimensions.Add(outputDim);
            weightDimensions.Add(input.Shape.Dimensions.Single());

            Parameter weights = new Parameter(NDShape.CreateNDShape(weightDimensions), DataType.Double,
                                              CNTKLib.GlorotUniformInitializer( // valeur par défaut aléatoire
                                                  CNTKLib.DefaultParamInitScale,
                                                  CNTKLib.SentinelValueForInferParamInitRank,
                                                  CNTKLib.SentinelValueForInferParamInitRank, 1));          // les coefficients à trouver

            Parameter bias = new Parameter(NDShape.CreateNDShape(new[] { outputDim }), DataType.Double, 0); // une abscisse pour chaque dimension

            Function ax        = CNTKLib.Times(weights, input);
            Function ax_plus_b = CNTKLib.Plus(bias, ax);

            return(ax_plus_b);
        }
Example #10
0
 Parameter Parameter(IEnumerable <int> dims)
 {
     return(new Parameter(NDShape.CreateNDShape(dims), DataType.Double,
                          CNTKLib.GlorotUniformInitializer( // valeur par défaut aléatoire
                              CNTKLib.DefaultParamInitScale,
                              CNTKLib.SentinelValueForInferParamInitRank,
                              CNTKLib.SentinelValueForInferParamInitRank, 1)));
 }
Example #11
0
        /// <summary>
        /// Based on Dense from: https://github.com/Microsoft/CNTK/blob/master/bindings/python/cntk/layers/layers.py
        /// </summary>
        public static Function Dense(this Function input,
                                     int units,
                                     CNTKDictionary weightInitializer,
                                     CNTKDictionary biasInitializer,
                                     DeviceDescriptor device,
                                     DataType dataType,
                                     int inputRank = 0,
                                     int mapRank   = 0)
        {
            if (inputRank != 0 && mapRank != 0)
            {
                throw new ArgumentException("Dense: inputRank and mapRank cannot be specified at the same time.");
            }

            var outputShape = NDShape.CreateNDShape(new int[] { units });
            var outputRank  = outputShape.Dimensions.Count;

            var inputRanks = (inputRank != 0) ? inputRank : 1;
            var dimensions = Enumerable.Range(0, inputRanks)
                             .Select(v => NDShape.InferredDimension).ToArray(); // infer all dimensions.
            var inputShape = NDShape.CreateNDShape(dimensions);

            int inferInputRankToMap;

            if (inputRank != 0)
            {
                inferInputRankToMap = -1; // means map_rank is not specified; input_rank rules.
            }
            else if (mapRank == 0)
            {
                inferInputRankToMap = 0;  // neither given: default to 'infer W to use all input dims'.
            }
            else
            {
                inferInputRankToMap = mapRank;  // infer W to use all input dims except the first static 'map_rank' ones.
            }

            var weightsDimensions = outputShape.Dimensions.ToList();

            weightsDimensions.AddRange(inputShape.Dimensions);
            var weightsShape = NDShape.CreateNDShape(weightsDimensions);

            var weights = new Parameter(weightsShape, dataType, weightInitializer, device, "w");

            // Weights and input is in reversed order compared to the original python code.
            // Same goes for the dimensions. This is because the python api reverses the dimensions internally.
            // The python API was made in this way to be similar to other deep learning toolkits.
            // The C# and the C++ share the same column major layout.
            var r = CNTKLib.Times(weights, input, (uint)outputRank, inferInputRankToMap);

            if (biasInitializer != null)
            {
                var biasParameter = new Parameter(outputShape, dataType, biasInitializer, device, "b");
                r = r + biasParameter;
            }

            return(r);
        }
Example #12
0
        public void cntk_partial_shape_test()
        {
            // Note: Keras/TensorFlow represent unknown dimensions
            // as None, whereas TensorFlowSharp represents as -1:

            /*
             *  import keras
             *
             *  from keras.models import Sequential
             *  from keras.layers import Dense
             *  from keras import backend as K
             *  import numpy as np
             *
             *  a = K.placeholder(shape = (None, 2))
             *  b = K.variable(np.matrix([[1, 2, 3], [4, 5, 6]]))
             *  ab = K.dot(a, b)
             *
             *  shape_a = K.int_shape(a)
             *  shape_b = K.int_shape(b)
             *  shape_ab = K.int_shape(ab)
             *
             *  print(shape_a)
             *  print(shape_b)
             *  print(shape_ab)
             *
             *  >>> Using TensorFlow backend.
             *  (None, 2)
             *  (2, 3)
             *  (None, 3)
             */

            using (var K = new CNTKBackend())
            {
                Tensor a = K.placeholder(shape: new int?[] { null, 2 });
                Tensor b = K.variable(array: new float[, ] {
                    { 1, 2, 3 },
                    { 4, 5, 6 }
                });

                var ab = K.dot(a, b);

                int?[] shape_a  = K.int_shape(a);
                int?[] shape_b  = K.int_shape(b);
                int?[] shape_ab = K.int_shape(ab);

                NDShape tf_shape_a  = K.In(a).CNTK_Shape;
                NDShape tf_shape_b  = K.In(b).CNTK_Shape;
                NDShape tf_shape_ab = K.In(ab).CNTK_Shape;

                AssertEx.AreEqual(new int?[] { null, 2 }, shape_a);
                AssertEx.AreEqual(new int?[] { 2, 3 }, shape_b);
                AssertEx.AreEqual(new int?[] { null, 3 }, shape_ab);

                Assert.AreEqual(NDShape.CreateNDShape(new[] { NDShape.FreeDimension, 2 }), tf_shape_a);
                Assert.AreEqual(NDShape.CreateNDShape(new[] { 2, 3 }), tf_shape_b);
                Assert.AreEqual(NDShape.CreateNDShape(new[] { NDShape.FreeDimension, 3 }), tf_shape_ab);
            }
        }
Example #13
0
        internal static Function Conv(this Function input,
                                      int[] filterShape,
                                      int filterCount,
                                      int[] filterStride,
                                      Padding padding, // TODO: Consider if padding should be decided pr. dimension.
                                      CNTKDictionary weightInitializer,
                                      CNTKDictionary biasInitializer,
                                      DeviceDescriptor device,
                                      DataType dataType)
        {
            var weights = new Parameter(NDShape.CreateNDShape(filterShape), dataType,
                                        weightInitializer, device);

            var strideShape = NDShape.CreateNDShape(filterStride);

            // Currently, only sharing=true is supported by CNTK. So these are hardcoded.
            // sharing dimensions follows stride dimensions. 1D, 2D, 3D, etc.
            var sharing = CntkUtilities.CreateFilledBoolVector(filterStride.Length, true);

            // Padding dimensions follows stride dimensions. 1D, 2D, 3D, etc.
            var usePadding  = padding.ToBoolean();
            var autoPadding = CntkUtilities.CreateFilledBoolVector(filterStride.Length, usePadding);

            // TODO: Consider if we want to surface the additional options for Convolution:
            // - dilation
            // - reductionRank
            // - groups
            // - maxTempMemSizeInSamples

            // Default for dilation seems to be a shape of size (1) with value 1
            var dilation = NDShape.CreateNDShape(new[] { 1 });

            // Following are defaults extrapolated from CNTK code
            var reductionRank           = 1u;
            var groups                  = 1u;
            var maxTempMemSizeInSamples = 0u;
            var sequential              = false;

            var result = CNTKLib.Convolution(
                weights, input, strideShape, sharing, autoPadding,
                dilation, reductionRank, groups, maxTempMemSizeInSamples, sequential);

            if (biasInitializer != null)
            {
                // Bias dimensions should be defined for filter dimensions.
                // For instance for 2D case: (1, 1, filterChannels).
                var biasShape = filterStride.Select(s => 1).ToList();
                biasShape.Add(filterCount);

                var bias = new Parameter(NDShape.CreateNDShape(biasShape.ToArray()),
                                         dataType, biasInitializer, device);

                result = CNTKLib.Plus(result, bias);
            }

            return(result);
        }
Example #14
0
        /// <summary>
        ///   Turn a nD tensor into a 2D tensor with same 0th dimension. In other words, it flattens each data samples of a batch.
        /// </summary>
        ///
        public Tensor batch_flatten(Tensor x)
        {
            // https://github.com/fchollet/keras/blob/f65a56fb65062c8d14d215c9f4b1015b97cc5bf3/keras/backend/cntk_backend.py#L1460
            // cntk's batch axis is not in shape,
            // so just flatten all the dim in x.shape
            int dim = Matrix.Product(x.shape.Select(s => s.Value).ToArray());

            x = Out(C.Reshape(In(x), NDShape.CreateNDShape(new[] { -1 })));
            x._keras_shape = new int?[] { null, dim };
            return(x);
        }
Example #15
0
        private static Function LinearLayer(Variable input, int ouputDims)
        {
            int       nbDimensionsInput = input.Shape[0];
            Parameter bias    = new Parameter(NDShape.CreateNDShape(new[] { ouputDims }), DataType.Double, 0);                    // une abscisse pour chaque dimension
            Parameter weights = new Parameter(NDShape.CreateNDShape(new[] { nbDimensionsInput, ouputDims }), DataType.Double, 0); // les coefficients à trouver
            // 2 variable d'input, 2 estimations en sortie (proba vrai et proba faux)

            Function linearLayer = CNTKLib.Plus(CNTKLib.Times(weights, input), bias);

            return(linearLayer);
        }
Example #16
0
        public Tensor bias_add(Tensor output, Tensor bias)
        {
            CNTKTensor _x     = In(output);
            CNTKTensor _b     = In(bias);
            var        _shape = In(_x.CNTK_Shape).Select(x => x < 0 ? 1 : x).ToArray();

            var shape = NDShape.CreateNDShape(_shape);

            var b = C.Reshape(_b, shape);

            return(Out(new Variable(_x.function) + b));
        }
        public void TestShapeSpecialDimensions()
        {
            NDShape shapeWithInferredDimension = NDShape.CreateNDShape(new int[] { 3, 7, NDShape.InferredDimension });

            Assert.IsTrue(shapeWithInferredDimension.HasInferredDimension);
            Assert.AreEqual(shapeWithInferredDimension[2], NDShape.InferredDimension);

            NDShape shapeWithFreeDimension = NDShape.CreateNDShape(new int[] { 3, 7, NDShape.FreeDimension });

            Assert.IsTrue(shapeWithFreeDimension.HasFreeDimension);
            Assert.AreEqual(shapeWithFreeDimension[2], NDShape.FreeDimension);
        }
Example #18
0
        public static Function BatchNorm(this Function input,
                                         BatchNorm batchNorm,
                                         DeviceDescriptor device,
                                         DataType dataType,
                                         double initialScaleValue      = 1,
                                         double initialBiasValue       = 0,
                                         int normalizationTimeConstant = 5000)
        {
            var inferredDimension1D = NDShape.CreateNDShape(new int[] { NDShape.InferredDimension });

            var scaleInitializer = CNTKLib.ConstantInitializer(initialScaleValue);
            var scaleParams      = new Parameter(inferredDimension1D,
                                                 dataType, scaleInitializer, device);

            var biasInitializer = CNTKLib.ConstantInitializer(initialBiasValue);
            var biasParams      = new Parameter(inferredDimension1D,
                                                dataType, biasInitializer, device);

            const double zeroInit = 1.0;

            // Batch normalization initial state are constants.
            var runningMean   = new Constant(inferredDimension1D, dataType, zeroInit, device);
            var runningInvStd = new Constant(inferredDimension1D, dataType, zeroInit, device);
            var runningCount  = new Constant(NDShape.CreateNDShape(new[] { 1 }), dataType, zeroInit, device);

            bool spatial = batchNorm == LayerFunctions.BatchNorm.Spatial;

            // Allows to smooth batch estimates with the running statistics.
            // However, this has not been found useful so far in our experiments (from CNTK team).
            const double blendTimeConstant = 0.0;

            // Epsilon is added to the variance to avoid division by 0.
            const double epsilon  = 0.00001;
            bool         useCudnn = device.Type == DeviceKind.GPU;
            const bool   disableRegularization = false;

            // TODO: Consider if we want to surface the additional options for BatchNorm:
            // - blendTimeConstant
            // - epsilon
            // - useCudnn
            // - disableRegularization
            // - name
            return(CNTKLib.BatchNormalization(input,
                                              scaleParams, biasParams,
                                              runningMean, runningInvStd, runningCount,
                                              spatial,
                                              normalizationTimeConstant, blendTimeConstant,
                                              epsilon,
                                              useCudnn,
                                              disableRegularization));
        }
Example #19
0
        public void Run()
        {
            var device = DeviceDescriptor.UseDefaultDevice();

            int nbObservationsInItem             = 5;
            List <Example_106_Data> allData      = GenerateExampleData(10000, nbObservationsInItem, 5);
            List <Example_106_Data> trainingData = allData.Take(8000).ToList();
            List <Example_106_Data> evalData     = allData.Skip(8000).ToList();

            int[]    inputDim = new int[] { 1 };
            Variable input    = Variable.InputVariable(NDShape.CreateNDShape(inputDim), DataType.Double, "input", dynamicAxes: new List <Axis>()
            {
                Axis.DefaultDynamicAxis(), Axis.DefaultBatchAxis()
            });                                                                                                                                                                                      // default dynamic axis ???
            int outputDim = 1;

            Function model = DefineModel_LSTM(input, nbObservationsInItem, outputDim);

            Function output = model.Output;

            //Function output = Variable.InputVariable(NDShape.CreateNDShape(new[] { 1 }), DataType.Float, "output", model.Output.DynamicAxes);

            //IEnumerable<int> inputSequence = ...; // pas clair
            //Value sequence = Value.CreateSequence(NDShape.CreateNDShape(new int[] { 1 }), inputSequence, device);

            uint minibatchSize = 100;

            Variable expectedOutput = Variable.InputVariable(NDShape.CreateNDShape(new int[] { outputDim }), DataType.Double, "expectedOutput", dynamicAxes: new List <Axis>()
            {
                Axis.DefaultBatchAxis()
            });                                                                                                                                                                                             // default dynamic axis ???
            Trainer trainer = MakeTrainer(expectedOutput, output, model, minibatchSize);

            {   // train
                int nbSamplesToUseForTraining = trainingData.Count;
                int numSweepsToTrainWith      = 20;
                int numMinibatchesToTrain     = nbSamplesToUseForTraining * numSweepsToTrainWith / (int)minibatchSize;
                var trainingInput             = trainingData.Select(x => x.Observations.Select(y => y)).ToList();
                var trainingOutput            = trainingData.Select(x => new[] { x.ExpectedPrediction }).ToList();
                var trainingMinibatchSource   = new GenericMinibatchSequenceSource(input, trainingInput, expectedOutput, trainingOutput, nbSamplesToUseForTraining, numSweepsToTrainWith, minibatchSize, device);
                RunTraining(trainer, trainingMinibatchSource, numMinibatchesToTrain, device);
            }

            // evaluate
            Evaluate(model, evalData, input, device);
        }
Example #20
0
        public static Value ToValue(this IEnumerable <Vector3> vectors, DeviceDescriptor device)
        {
            var count = vectors.Count();

            Assert.AreNotEqual(count, 0, "Empty list");
            float[] floatArray = new float[count * 3];
            NDShape shape      = NDShape.CreateNDShape(new int[] { 3 });

            Parallel.For(0, count, (int batchNum) =>
            {
                var vector = vectors.ElementAt(batchNum);
                floatArray[batchNum * 3]     = vector.x;
                floatArray[batchNum * 3 + 1] = vector.y;
                floatArray[batchNum * 3 + 2] = vector.z;
            });
            return(Value.CreateBatch(shape, floatArray, device, false));
        }
Example #21
0
        public static Value ToValue(this IEnumerable <Quaternion> quats, DeviceDescriptor device)
        {
            Assert.AreNotEqual(quats.Count(), 0);
            float[] floatArray = new float[quats.Count()];
            NDShape shape      = NDShape.CreateNDShape(new int[] { 4 });
            int     count      = quats.Count();

            Parallel.For(0, count, (int batchNum) =>
            {
                var quat = quats.ElementAt(batchNum);
                floatArray[batchNum * 4]     = quat.w;
                floatArray[batchNum * 4 + 1] = quat.x;
                floatArray[batchNum * 4 + 2] = quat.y;
                floatArray[batchNum * 4 + 3] = quat.z;
            });
            return(Value.CreateBatch(shape, floatArray, device, false));
        }
Example #22
0
        public Tensor placeholder(int?[] shape = null, int?ndim = null, DataType?dtype = null, bool sparse = false, string name = null)
        {
            log(new { shape, ndim, dtype, sparse, name });

            // https://github.com/fchollet/keras/blob/f65a56fb65062c8d14d215c9f4b1015b97cc5bf3/keras/backend/cntk_backend.py
            if (shape == null)
            {
                if (ndim != null)
                {
                    shape = new int?[ndim.Value];
                }
            }

            var cntk_shape = shape.Select(s => s == null ? NDShape.FreeDimension : s.Value);

            //if (dynamic_axis_num > len(cntk_shape)
            //{
            //    raise ValueError('CNTK backend: creating placeholder with '
            //            '%d dimension is not supported, at least '
            //            '%d dimensions are needed.'
            //            % (len(cntk_shape, dynamic_axis_num)))
            //}

            if (dtype == null)
            {
                dtype = floatx();
            }

            if (name is null)
            {
                name = String.Empty;
            }

            // cntk_shape = cntk_shape[dynamic_axis_num:]

            NDShape      _shape = NDShape.CreateNDShape(cntk_shape);
            CNTKDataType _dtype = In(dtype.Value);
            Variable     v      = Variable.InputVariable(shape: _shape, dataType: _dtype, isSparse: sparse, name: name);

            var x = Out(v);

            x._keras_shape         = shape;
            x._uses_learning_phase = false;
            return(x);
        }
Example #23
0
        public Tensor bias_add(Tensor output, Tensor bias, DataFormatType?data_format = null, string name = null)
        {
            if (data_format != null)
            {
                throw new NotImplementedException();
            }

            using (this.name_scope("bias_add"))
            {
                CNTKTensor _x     = In(output);
                CNTKTensor _b     = In(bias);
                var        _shape = In(_x.CNTK_Shape).Select(x => x < 0 ? 1 : x).ToArray();

                var shape = NDShape.CreateNDShape(_shape);

                var b = C.Reshape(_b, shape);

                return(Out(new Variable(_x.function) + b));
            }
        }
Example #24
0
        /// <summary>
        /// Create embedding sequence
        /// </summary>
        /// <param name="input"></param>
        /// <param name="embeddingDim"></param>
        /// <param name="device"></param>
        /// <returns></returns>
        public Function Embedding(Variable input, int embeddingDim)
        {
            //checking the dimension of the input variable
            //it has to be always a vector which represents the tensor with rank=1
            System.Diagnostics.Debug.Assert(input.Shape.Rank == 1);

            //extract input dimensions from the input variable
            int inputDim = input.Shape[0];

            //initialization of the default parameters
            var glorotInit = CNTKLib.GlorotUniformInitializer();

            //create ND shape and parameter
            var dims         = new int[] { embeddingDim, inputDim };
            var embededParam = new Parameter(NDShape.CreateNDShape(dims), DataType.Float, glorotInit, m_device);

            Function embededL = CNTKLib.Times(embededParam, input, "embedded_" + input.Name);

            return(embededL);
        }
Example #25
0
        private static double[][] sum(params int[] axes)
        {
            var arr = new[]
            {
                /*                            total:
                 * /*       */1.0, 2.0, 3.0, /*  6.0 */
                /*       */ 4.0, 5.0, 6.0, /* 15.0 */
                /* total:   5.0, 7.0, 9.0     21.0 */
            };

            var      shape = NDShape.CreateNDShape(new[] { 2, 3 });
            Value    vx    = Value.CreateBatch(shape, arr, DeviceDescriptor.CPUDevice, readOnly: true);
            Variable x     = Variable.InputVariable(shape, CNTK.DataType.Double, name: "input");

            CNTK.Function f;
            if (axes == null)
            {
                f = CNTKLib.Alias(x);
            }
            else
            {
                var axisVector = new AxisVector(axes.Select(ax => new Axis(ax)).ToArray());
                f = CNTKLib.ReduceSum(x, axis: axisVector);
            }

            var inputs = new Dictionary <Variable, Value>()
            {
                { x, vx }
            };
            var outputs = new Dictionary <Variable, Value>()
            {
                { f, null }
            };

            f.Evaluate(inputs, outputs, DeviceDescriptor.CPUDevice);
            var r = outputs[f].GetDenseData <double>((Variable)f);

            return(r.Select(ri => ri.ToArray()).ToArray());
        }
Example #26
0
        internal Function Convolution(Variable input, int[] filterSize, int nbFilter, int[] strides, Func <Variable, Function> activation)
        {
            int numInputChannels = input.Shape.Dimensions.Last();

            List <int> kernelDims = new List <int>();

            kernelDims.AddRange(filterSize);
            kernelDims.Add(numInputChannels);
            kernelDims.Add(nbFilter);

            List <int> strideDims = new List <int>();

            strideDims.AddRange(strides);
            strideDims.Add(numInputChannels);

            var convParams = Parameter(kernelDims);
            var conv       = CNTKLib.Convolution(convParams, input, NDShape.CreateNDShape(strideDims));

            if (activation == null)
            {
                return(conv);
            }
            return(activation(conv));
        }
Example #27
0
        Function Generator(Function input, Func <CNTKDictionary> weightInit, CNTKDictionary biasInit,
                           DeviceDescriptor device, DataType dataType)
        {
            var generatorNetwork = input
                                   .Dense(1024, weightInit(), biasInit, device, dataType)
                                   .BatchNorm(BatchNorm.Regular, device, dataType)
                                   .ReLU()

                                   .Dense(7 * 7 * 128, weightInit(), biasInit, device, dataType)
                                   .BatchNorm(BatchNorm.Regular, device, dataType)
                                   .ReLU()
                                   .Reshape(NDShape.CreateNDShape(new int[] { 7, 7, 128 }))

                                   .ConvTranspose2D((5, 5), 128, (2, 2), Padding.Zeros, (14, 14), weightInit(), biasInit, device, dataType)
                                   .BatchNorm(BatchNorm.Spatial, device, dataType)
                                   .ReLU()

                                   .ConvTranspose2D((5, 5), 1, (2, 2), Padding.Zeros, (28, 28), weightInit(), biasInit, device, dataType)
                                   .Tanh();

            Trace.Write(Model.Summary(generatorNetwork));

            return(generatorNetwork.Reshape(NDShape.CreateNDShape(new int[] { 784 })));
        }
Example #28
0
        public void Run()
        {
            var device = DeviceDescriptor.UseDefaultDevice();
            var util   = new Example_103_Util();

            Example_201_Data datasource = new Example_201_Data();
            IEnumerable <Example_201_Item> trainingImages = datasource.LoadTrainingImages().ToList();
            IEnumerable <Example_201_Item> testImages     = datasource.LoadTestImages().ToList();
            IDictionary <double, string>   labelIndex     = datasource.LoadLabelIndex().ToDictionary(x => (double)x.Key, x => x.Value);

            int image_height = 32, image_width = 32, num_channels = 3, num_classes = 10;

            Variable input          = Variable.InputVariable(NDShape.CreateNDShape(new[] { image_height, image_width, num_channels }), DataType.Double, "input");
            Variable expectedOutput = Variable.InputVariable(new int[] { num_classes }, DataType.Double, "expectedOutput");

            Function normalizedInput = CNTKLib.ElementTimes(Constant.Scalar(1.0 / 255.0, device), input);
            Function model           = DefineModel_C(normalizedInput, num_classes, util);

            Variable output = model.Output;

            uint    minibatchSize = 64;
            Trainer trainer       = MakeTrainer(expectedOutput, output, model, minibatchSize);

            {   // train
                int nbSamplesToUseForTraining = trainingImages.Count();
                int numSweepsToTrainWith      = 5;
                int numMinibatchesToTrain     = nbSamplesToUseForTraining * numSweepsToTrainWith / (int)minibatchSize;
                var trainingInput             = trainingImages.Select(x => x.Image.Select(y => (double)y).ToArray()).ToList();
                var trainingOutput            = trainingImages.Select(x => ToOneHotVector(x.Label, labelIndex.Count)).ToList();
                var trainingMinibatchSource   = new GenericMinibatchSource(input, trainingInput, expectedOutput, trainingOutput, nbSamplesToUseForTraining, numSweepsToTrainWith, minibatchSize, device);
                RunTraining(trainer, trainingMinibatchSource, numMinibatchesToTrain, device);
            }

            // evaluate
            Evaluate(model, testImages, input, device, labelIndex);
        }
Example #29
0
        public void Run()
        {
            // Prepare data
            var baseDataDirectoryPath = @"E:\DataSets\Mnist";
            var trainFilePath         = Path.Combine(baseDataDirectoryPath, "Train-28x28_cntk_text.txt");

            // Define data type and device for the model.
            var dataType = DataType.Float;
            var device   = DeviceDescriptor.UseDefaultDevice();

            // Setup initializers
            var random = new Random(232);
            Func <CNTKDictionary> weightInit = () => Initializers.Xavier(random.Next(), scale: 0.02);
            var biasInit = Initializers.Zero();

            // Ensure reproducible results with CNTK.
            CNTKLib.SetFixedRandomSeed((uint)random.Next());
            CNTKLib.ForceDeterministicAlgorithms();

            // Setup generator
            var ganeratorInputShape = NDShape.CreateNDShape(new int[] { 100 });
            var generatorInput      = Variable.InputVariable(ganeratorInputShape, dataType);
            var generatorNetwork    = Generator(generatorInput, weightInit, biasInit, device, dataType);

            // Setup discriminator
            var discriminatorInputShape = NDShape.CreateNDShape(new int[] { 784 }); // 28 * 28 * 1.
            var discriminatorInput      = Variable.InputVariable(discriminatorInputShape, dataType);
            // scale image input between -1.0 and 1.0.
            var discriminatorInputScaled = CNTKLib.Minus(
                CNTKLib.ElementTimes(Constant.Scalar(2 * 0.00390625f, device), discriminatorInput),
                Constant.Scalar(1.0f, device));

            var discriminatorNetwork = Discriminator(discriminatorInputScaled, weightInit, biasInit, device, dataType);

            // The discriminator must be used on both the real MNIST images and fake images generated by the generator function.
            // One way to represent this in the computational graph is to create a clone of the output of the discriminator function,
            // but with substituted inputs. Setting method = share in the clone function ensures that both paths through the discriminator model
            // use the same set of parameters.
            var discriminatorNetworkFake = discriminatorNetwork
                                           .Clone(ParameterCloningMethod.Share, replacements:
                                                  new Dictionary <Variable, Variable>
            {
                { discriminatorInputScaled.Output, generatorNetwork.Output },
            });

            // Create minibatch source for providing the real images.
            var imageNameToVariable = new Dictionary <string, Variable> {
                { "features", discriminatorInput }
            };
            var imageMinibatchSource = CreateMinibatchSource(trainFilePath, imageNameToVariable, randomize: true);

            // Create minibatch source for providing the noise.
            var noiseNameToVariable = new Dictionary <string, Variable> {
                { "noise", generatorInput }
            };
            var noiseMinibatchSource = new UniformNoiseMinibatchSource(noiseNameToVariable, min: -1.0f, max: 1.0f, seed: random.Next());

            // Combine both sources in the composite minibatch source.
            var compositeMinibatchSource = new CompositeMinibatchSource(imageMinibatchSource, noiseMinibatchSource);

            // Setup generator loss: 1.0 - C.log(D_fake)
            var generatorLossFunc = CNTKLib.Minus(Constant.Scalar(1.0f, device),
                                                  CNTKLib.Log(discriminatorNetworkFake));

            // Setup discriminator loss: -(C.log(D_real) + C.log(1.0 - D_fake))
            var discriminatorLossFunc = CNTKLib.Negate(CNTKLib.Plus(CNTKLib.Log(discriminatorNetwork),
                                                                    CNTKLib.Log(CNTKLib.Minus(Constant.Scalar(1.0f, device), discriminatorNetworkFake))));

            // Create fitters for the training loop.
            // Generator uses Adam and discriminator SGD.
            // Advice from: https://github.com/soumith/ganhacks
            var generatorLearner = Learners.Adam(generatorNetwork.Parameters(),
                                                 learningRate: 0.0002, momentum: 0.5, gradientClippingThresholdPerSample: 1.0);
            var generatorFitter = CreateFitter(generatorLearner, generatorNetwork, generatorLossFunc, device);

            var discriminatorLearner = Learners.SGD(discriminatorNetwork.Parameters(),
                                                    learningRate: 0.0002, gradientClippingThresholdPerSample: 1.0);
            var discriminatorFitter = CreateFitter(discriminatorLearner, discriminatorNetwork, discriminatorLossFunc, device);

            int epochs    = 30;
            int batchSize = 128;

            // Controls how many steps the discriminator takes,
            // each time the generator takes 1 step.
            // Default from the original paper is 1.
            int discriminatorSteps = 1;

            var isSweepEnd = false;

            for (int epoch = 0; epoch < epochs;)
            {
                for (int step = 0; step < discriminatorSteps; step++)
                {
                    // Discriminator needs both real images and noise,
                    // so uses the composite minibatch source.
                    var minibatchItems = compositeMinibatchSource.GetNextMinibatch(batchSize, device);
                    isSweepEnd = minibatchItems.isSweepEnd;

                    discriminatorFitter.FitNextStep(minibatchItems.minibatch, batchSize);
                    DisposeValues(minibatchItems.minibatch);
                }

                // Generator only needs noise images,
                // so uses the noise minibatch source separately.
                var noiseMinibatchItems = noiseMinibatchSource.GetNextMinibatch(batchSize, device);

                generatorFitter.FitNextStep(noiseMinibatchItems.minibatch, batchSize);
                DisposeValues(noiseMinibatchItems.minibatch);

                if (isSweepEnd)
                {
                    var generatorCurrentLoss = generatorFitter.CurrentLoss;
                    generatorFitter.ResetLossAndMetricAccumulators();

                    var discriminatorCurrentLoss = discriminatorFitter.CurrentLoss;
                    discriminatorFitter.ResetLossAndMetricAccumulators();

                    var traceOutput = $"Epoch: {epoch + 1:000} Generator Loss = {generatorCurrentLoss:F8}, Discriminator Loss = {discriminatorCurrentLoss:F8}";
                    Trace.WriteLine(traceOutput);

                    ++epoch;
                }
            }

            // Sample 6x6 images from generator.
            var samples = 6 * 6;
            var batch   = noiseMinibatchSource.GetNextMinibatch(samples, device);
            var noise   = batch.minibatch;

            var predictor  = new Predictor(generatorNetwork, device);
            var images     = predictor.PredictNextStep(noise);
            var imagesData = images.SelectMany(t => t).ToArray();

            // Show examples
            var app    = new Application();
            var window = new PlotWindowBitMap("Generated Images", imagesData, 28, 28, 1, true);

            window.Show();
            app.Run(window);
        }
Example #30
0
        public void Run()
        {
            var device = DeviceDescriptor.UseDefaultDevice();

            var util = new Example_103_Util();

            // data
            string trainImagesPath = "./Example_103/train-images-idx3-ubyte.gz";
            //string trainLabelsPath = "./Example_103/train-labels-idx1-ubyte.gz";
            List <double[]> trainImages = util.LoadImages(trainImagesPath).Select(x => x.Select(y => (double)y).ToArray()).ToList();
            //List<int> trainLabels = util.LoadLabels(trainLabelsPath);
            //List<double[]> trainLabels1Hot = trainLabels.Select(x => util.ConvertTo1Hot(x)).Select(x => x.Cast<double>().ToArray()).ToList();

            string evelImagesPath = "./Example_103/t10k-images-idx3-ubyte.gz";
            //string evalLabelsPath = "./Example_103/t10k-labels-idx1-ubyte.gz";
            List <double[]> evalImages = util.LoadImages(evelImagesPath).Select(x => x.Select(y => (double)y).ToArray()).ToList();
            //List<int> evalLabels = util.LoadLabels(evalLabelsPath);
            //List<int[]> evalLabels1Hot = evalLabels.Select(x => util.ConvertTo1Hot(x)).ToList();

            // model

            int sampleSize        = trainImages.Count;
            int nbDimensionsInput = 28 * 28;

            Variable inputVariables = Variable.InputVariable(NDShape.CreateNDShape(new [] { nbDimensionsInput }), DataType.Double, "input");
            Variable expectedOutput = Variable.InputVariable(NDShape.CreateNDShape(new [] { nbDimensionsInput }), DataType.Double, "output");

            Function encodeDecode = DefineModel_104B(util, inputVariables, device);

            //var scaledModelOutput = CNTKLib.ElementTimes(Constant.Scalar<double>(1.0 / 255.0, device), encodeDecode);
            //var scaledExpectedOutput = CNTKLib.ElementTimes(Constant.Scalar<double>(1.0 / 255.0, device), expectedOutput);

            //{

            //    Function test = CNTKLib.ElementTimes(
            //                Constant.Scalar(-1.0d, device),
            //                inputVariables);


            //}


            //Function lossFunction = -scaledExpectedOutput * CNTKLib.Log(scaledModelOutput) - (Constant.Scalar(-1.0d, device) - scaledExpectedOutput) * CNTKLib.Log(1 - scaledModelOutput);

            var scaledExpectedOutput = CNTKLib.ElementTimes(expectedOutput, Constant.Scalar(1 / 255.0, device));
            //Function lossFunction = CNTKLib.CrossEntropyWithSoftmax(encodeDecode, expectedOutput);

            // Function lossFunction = CNTKLib.CrossEntropyWithSoftmax(scaledModelOutput, scaledExpectedOutput);
            Function lossFunction = CNTKLib.Square(CNTKLib.Minus(scaledExpectedOutput, encodeDecode));

            Function evalErrorFunction = CNTKLib.ClassificationError(encodeDecode, scaledExpectedOutput);

            // training

            Trainer trainer;
            {
                // define training
                //int epochSize = 30000;
                uint minibatchSize = 64;
                //double learningRate = 0.8;
                int numSweepsToTrainWith      = 2;     // traduction de sweep ?
                int nbSamplesToUseForTraining = 60000; // trainImages.Count;

                double lr_per_sample = 0.2;
                //double lr_per_sample = 0.2; // 0.00003;
                //double lr_per_sample = 0.00003; // 0.00003;
                uint epoch_size = 30000; //        # 30000 samples is half the dataset size

                TrainingParameterScheduleDouble learningRatePerSample = new TrainingParameterScheduleDouble(lr_per_sample, epoch_size);
                TrainingParameterScheduleDouble momentumSchedule      = new TrainingParameterScheduleDouble(0.9126265014311797, minibatchSize);

                var parameters = new ParameterVector();
                foreach (var p in encodeDecode.Parameters())
                {
                    parameters.Add(p);
                }

                List <Learner> parameterLearners = new List <Learner>()
                {
                    CNTKLib.FSAdaGradLearner(parameters, learningRatePerSample, momentumSchedule, true)
                };
                //IList<Learner> parameterLearners = new List<Learner>() { Learner.SGDLearner(encodeDecode.Parameters(), learningRatePerSample) };
                trainer = Trainer.CreateTrainer(encodeDecode, lossFunction, evalErrorFunction, parameterLearners);

                // run training

                int numMinibatchesToTrain = nbSamplesToUseForTraining * numSweepsToTrainWith / (int)minibatchSize;

                var minibatchSource = new GenericMinibatchSource(inputVariables, trainImages, expectedOutput, trainImages, nbSamplesToUseForTraining, numSweepsToTrainWith, minibatchSize, device);

                double aggregate_metric = 0;
                for (int minibatchCount = 0; minibatchCount < numMinibatchesToTrain; minibatchCount++)
                {
                    IDictionary <Variable, MinibatchData> data = minibatchSource.GetNextRandomMinibatch();
                    trainer.TrainMinibatch(data, device);

                    double samples = trainer.PreviousMinibatchSampleCount();
                    double avg     = trainer.PreviousMinibatchEvaluationAverage();
                    aggregate_metric += avg * samples;
                    double nbSampleSeen = trainer.TotalNumberOfSamplesSeen();
                    double train_error  = aggregate_metric / nbSampleSeen;
                    Debug.WriteLine($"{minibatchCount} Average training error: {train_error:p2}");
                }
            }

            // evaluate
            {
                uint testMinibatchSize     = 32;
                int  nbSamplesToTest       = 32;// evalImages.Count;
                int  numMinibatchesToTrain = nbSamplesToTest / (int)testMinibatchSize;

                double metric_numer = 0;
                double metric_denom = 0;

                var minibatchSource = new GenericMinibatchSource(inputVariables, evalImages, expectedOutput, evalImages, nbSamplesToTest, 1, testMinibatchSize, device);
                for (int minibatchCount = 0; minibatchCount < numMinibatchesToTrain; minibatchCount++)
                {
                    IDictionary <Variable, MinibatchData> data = minibatchSource.GetNextRandomMinibatch();

                    ////UnorderedMapVariableMinibatchData evalInput = new UnorderedMapVariableMinibatchData();
                    ////foreach (var row in data)
                    ////    evalInput[row.Key] = row.Value;

                    ////double error = trainer.TestMinibatch(evalInput, device);

                    ////metric_numer += Math.Abs(error * testMinibatchSize);
                    ////metric_denom += testMinibatchSize;

                    ////MinibatchData outputValue = evalInput[expectedOutput];

                    //IList<IList<double>> inputPixels = outputValue.data.GetDenseData<double>(inputVariables);

                    //IList<IList<double>> actualLabelSoftMax = outputValue.data.GetDenseData<double>(encodeDecode.Output);

                    //for (int i = 0; i < actualLabelSoftMax.Count; i++)
                    //    PrintBitmap(inputPixels[i], actualLabelSoftMax[i], i);

                    // var normalizedInput = CNTKLib.ElementTimes(Constant.Scalar<double>(1.0 / 255.0, device), inputVariables);

                    Dictionary <Variable, Value> input = new Dictionary <Variable, Value>()
                    {
                        { inputVariables, data[inputVariables].data }
                    };
                    Dictionary <Variable, Value> output = new Dictionary <Variable, Value>()
                    {
                        // { normalizedInput.Output, null }
                        { encodeDecode.Output, null }
                    };

                    encodeDecode.Evaluate(input, output, device);

                    IList <IList <double> > inputPixels  = input[inputVariables].GetDenseData <double>(inputVariables);
                    IList <IList <double> > outputPixels = output[encodeDecode.Output].GetDenseData <double>(encodeDecode.Output);
                    for (int i = 0; i < inputPixels.Count; i++)
                    {
                        PrintBitmap(inputPixels[i], outputPixels[i], i);
                    }
                }

                double test_error = (metric_numer * 100.0) / (metric_denom);
                Debug.WriteLine($"Average test error: {test_error:p2}");
            }
        }