static public void TrainAndEvaluate(DeviceDescriptor device) { // build a logistic regression model Variable featureVariable = Variable.InputVariable(new int[] { inputDim }, DataType.Float); Variable labelVariable = Variable.InputVariable(new int[] { numOutputClasses }, DataType.Float); var classifierOutput = CreateLinearModel(featureVariable, numOutputClasses, device); var loss = CNTKLib.CrossEntropyWithSoftmax(classifierOutput, labelVariable); var evalError = CNTKLib.ClassificationError(classifierOutput, labelVariable); // prepare for training CNTK.TrainingParameterScheduleDouble learningRatePerSample = new CNTK.TrainingParameterScheduleDouble( 0.02, TrainingParameterScheduleDouble.UnitType.Sample); IList <Learner> parameterLearners = new List <Learner>() { Learner.SGDLearner(classifierOutput.Parameters(), learningRatePerSample) }; var trainer = Trainer.CreateTrainer(classifierOutput, loss, evalError, parameterLearners); int minibatchSize = 64; int numMinibatchesToTrain = 1000; int updatePerMinibatches = 50; // train the model for (int minibatchCount = 0; minibatchCount < numMinibatchesToTrain; minibatchCount++) { Value features, labels; GenerateValueData(minibatchSize, inputDim, numOutputClasses, out features, out labels, device); trainer.TrainMinibatch( new Dictionary <Variable, Value>() { { featureVariable, features }, { labelVariable, labels } }, device); TestHelper.PrintTrainingProgress(trainer, minibatchCount, updatePerMinibatches); } // test and validate the model int testSize = 100; Value testFeatureValue, expectedLabelValue; GenerateValueData(testSize, inputDim, numOutputClasses, out testFeatureValue, out expectedLabelValue, device); // GetDenseData just needs the variable's shape IList <IList <float> > expectedOneHot = expectedLabelValue.GetDenseData <float>(labelVariable); IList <int> expectedLabels = expectedOneHot.Select(l => l.IndexOf(1.0F)).ToList(); var inputDataMap = new Dictionary <Variable, Value>() { { featureVariable, testFeatureValue } }; var outputDataMap = new Dictionary <Variable, Value>() { { classifierOutput.Output, null } }; classifierOutput.Evaluate(inputDataMap, outputDataMap, device); var outputValue = outputDataMap[classifierOutput.Output]; IList <IList <float> > actualLabelSoftMax = outputValue.GetDenseData <float>(classifierOutput.Output); var actualLabels = actualLabelSoftMax.Select((IList <float> l) => l.IndexOf(l.Max())).ToList(); int misMatches = actualLabels.Zip(expectedLabels, (a, b) => a.Equals(b) ? 0 : 1).Sum(); Console.WriteLine($"Validating Model: Total Samples = {testSize}, Misclassify Count = {misMatches}"); }
/// <summary> /// Train and evaluate a image classifier for MNIST data. /// </summary> /// <param name="device">CPU or GPU device to run training and evaluation</param> /// <param name="useConvolution">option to use convolution network or to use multilayer perceptron</param> /// <param name="forceRetrain">whether to override an existing model. /// if true, any existing model will be overridden and the new one evaluated. /// if false and there is an existing model, the existing model is evaluated.</param> public static void TrainAndEvaluate(DeviceDescriptor device, bool useConvolution, bool forceRetrain) { var featureStreamName = "features"; var labelsStreamName = "labels"; var classifierName = "classifierOutput"; Function classifierOutput; int[] imageDim = useConvolution ? new int[] { 28, 28, 1 } : new int[] { 784 }; int imageSize = 28 * 28; int numClasses = 10; IList <StreamConfiguration> streamConfigurations = new StreamConfiguration[] { new StreamConfiguration(featureStreamName, imageSize), new StreamConfiguration(labelsStreamName, numClasses) }; string modelFile = useConvolution ? "MNISTConvolution.model" : "MNISTMLP.model"; // If a model already exists and not set to force retrain, validate the model and return. if (File.Exists(modelFile) && !forceRetrain) { var minibatchSourceExistModel = MinibatchSource.TextFormatMinibatchSource( Path.Combine(ImageDataFolder, "Test_cntk_text.txt"), streamConfigurations); TestHelper.ValidateModelWithMinibatchSource(modelFile, minibatchSourceExistModel, imageDim, numClasses, featureStreamName, labelsStreamName, classifierName, device); return; } // build the network var input = CNTKLib.InputVariable(imageDim, DataType.Float, featureStreamName); if (useConvolution) { var scaledInput = CNTKLib.ElementTimes(Constant.Scalar <float>(0.00390625f, device), input); classifierOutput = CreateConvolutionalNeuralNetwork(scaledInput, numClasses, device, classifierName); } else { // For MLP, we like to have the middle layer to have certain amount of states. int hiddenLayerDim = 200; var scaledInput = CNTKLib.ElementTimes(Constant.Scalar <float>(0.00390625f, device), input); classifierOutput = CreateMLPClassifier(device, numClasses, hiddenLayerDim, scaledInput, classifierName); } var labels = CNTKLib.InputVariable(new int[] { numClasses }, DataType.Float, labelsStreamName); var trainingLoss = CNTKLib.CrossEntropyWithSoftmax(new Variable(classifierOutput), labels, "lossFunction"); var prediction = CNTKLib.ClassificationError(new Variable(classifierOutput), labels, "classificationError"); // prepare training data var minibatchSource = MinibatchSource.TextFormatMinibatchSource( Path.Combine(ImageDataFolder, "Train_cntk_text.txt"), streamConfigurations, MinibatchSource.InfinitelyRepeat); var featureStreamInfo = minibatchSource.StreamInfo(featureStreamName); var labelStreamInfo = minibatchSource.StreamInfo(labelsStreamName); // set per sample learning rate CNTK.TrainingParameterScheduleDouble learningRatePerSample = new CNTK.TrainingParameterScheduleDouble( 0.003125, TrainingParameterScheduleDouble.UnitType.Sample); IList <Learner> parameterLearners = new List <Learner>() { Learner.SGDLearner(classifierOutput.Parameters(), learningRatePerSample) }; var trainer = Trainer.CreateTrainer(classifierOutput, trainingLoss, prediction, parameterLearners); // const uint minibatchSize = 64; int outputFrequencyInMinibatches = 20, i = 0; int epochs = 5; while (epochs > 0) { var minibatchData = minibatchSource.GetNextMinibatch(minibatchSize, device); var arguments = new Dictionary <Variable, MinibatchData> { { input, minibatchData[featureStreamInfo] }, { labels, minibatchData[labelStreamInfo] } }; trainer.TrainMinibatch(arguments, device); TestHelper.PrintTrainingProgress(trainer, i++, outputFrequencyInMinibatches); // MinibatchSource is created with MinibatchSource.InfinitelyRepeat. // Batching will not end. Each time minibatchSource completes an sweep (epoch), // the last minibatch data will be marked as end of a sweep. We use this flag // to count number of epochs. if (TestHelper.MiniBatchDataIsSweepEnd(minibatchData.Values)) { epochs--; } } // save the trained model classifierOutput.Save(modelFile); // validate the model var minibatchSourceNewModel = MinibatchSource.TextFormatMinibatchSource( Path.Combine(ImageDataFolder, "Test_cntk_text.txt"), streamConfigurations, MinibatchSource.FullDataSweep); TestHelper.ValidateModelWithMinibatchSource(modelFile, minibatchSourceNewModel, imageDim, numClasses, featureStreamName, labelsStreamName, classifierName, device); }