static void Main(string[] args) { var context = new MLContext(); Console.WriteLine("Loading Data..."); var colDef = new TextLoader.Column[] { new TextLoader.Column(nameof(Digit.PixelValues), DataKind.Single, 1, 784), new TextLoader.Column("Number", DataKind.Single, 0) }; var trainDataView = context.Data.LoadFromTextFile(trainDataPath, colDef, hasHeader: true, separatorChar: ','); var testDataView = context.Data.LoadFromTextFile(testDataPath, colDef, hasHeader: true, separatorChar: ','); var pipeline = context.Transforms.Conversion.MapValueToKey(outputColumnName: "Label", inputColumnName: "Number", keyOrdinality: ValueToKeyMappingEstimator.KeyOrdinality.ByValue) .Append(context.Transforms.Concatenate("Features", nameof(Digit.PixelValues))) .AppendCacheCheckpoint(context) .Append(context.MulticlassClassification.Trainers.OneVersusAll(context.BinaryClassification.Trainers.FastForest(), "Label")) .Append(context.Transforms.Conversion.MapKeyToValue(outputColumnName: "Number", inputColumnName: "Label")); Console.WriteLine("Training the model..."); var model = pipeline.Fit(trainDataView); Console.WriteLine("Evaluating model..."); var predictions = model.Transform(testDataView); var metrics = context.MulticlassClassification.Evaluate(predictions, labelColumnName: "Number", scoreColumnName: "Score"); // show evaluation metrics Console.WriteLine($"Evaluation metrics"); Console.WriteLine($" MicroAccuracy: {metrics.MicroAccuracy:0.###}"); Console.WriteLine($" MacroAccuracy: {metrics.MacroAccuracy:0.###}"); Console.WriteLine($" LogLoss: {metrics.LogLoss:#.###}"); Console.WriteLine($" LogLossReduction: {metrics.LogLossReduction:#.###}"); Console.WriteLine(); var digits = context.Data.CreateEnumerable <Digit>(testDataView, false).ToArray(); var testDigits = new Digit[] { digits[215], // 0 digits[202], // 1 digits[199], // 2 digits[200], // 3 digits[198], // 4 digits[207], // 5 digits[201], // 6 digits[220], // 7 digits[226], // 8 digits[235] // 9 }; var engine = context.Model.CreatePredictionEngine <Digit, DigitPrediction>(model); var table = new BetterConsoleTables.Table(TableConfiguration.Unicode()); table.AddColumn("Digits"); for (var i = 0; i < 10; i++) { table.AddColumn($"P{i}"); } for (var i = 0; i < testDigits.Length; i++) { var prediction = engine.Predict(testDigits[i]); table.AddRow( testDigits[i].Number, prediction.Score[0].ToString("P2"), prediction.Score[1].ToString("P2"), prediction.Score[2].ToString("P2"), prediction.Score[3].ToString("P2"), prediction.Score[4].ToString("P2"), prediction.Score[5].ToString("P2"), prediction.Score[6].ToString("P2"), prediction.Score[7].ToString("P2"), prediction.Score[8].ToString("P2"), prediction.Score[9].ToString("P2")); } // show results Console.WriteLine(table.ToString()); }
static void Main(string[] args) { // Create machine learning context. var context = new MLContext(); // Let's load model if it already exists, otherwise let's create it. var modelPath = @"Assets\model.zip"; if (File.Exists(modelPath)) { // Load existing model. _model = context.Model.Load(modelPath, out DataViewSchema modelSchema); } else { // Upload training data set. LoadTrainingDigits(); // Load training data set to ML context. var trainingData = context.Data.LoadFromEnumerable(_trainingDigits.OrderBy(d => d.Number)); trainingData = context.Data.Cache(trainingData); // Define the trainer. // Different trainers give different accuracy of prediction, e.g.: // - LbfgsMaximumEntropy gives better accuracy but lasts longer (92%, > 10 min); // - SdcaMaximumEntropy gives worse accuracy, but lasts shorter (87%, < 1 min). var pipeline = context.Transforms.Conversion.MapValueToKey(inputColumnName: "Number", outputColumnName: "Label") .Append(context.Transforms.Concatenate("Features", "Pixels")) .Append(context.MulticlassClassification.Trainers.SdcaMaximumEntropy(labelColumnName: "Label", featureColumnName: "Features")) .Append(context.Transforms.Conversion.MapKeyToValue(inputColumnName: "PredictedLabel", outputColumnName: "PredictedNumber")); // Train the model. _model = pipeline.Fit(trainingData); // Save the model. context.Model.Save(_model, trainingData.Schema, modelPath); } #region Evaluate model uging test data set // Upload test data set. LoadTestDigits(); // Load test data set to ML context. var testData = context.Data.LoadFromEnumerable(_testDigits); testData = context.Data.Cache(testData); // Perform prediction on test data. var predictions = _model.Transform(testData); // Evaluate the model using prediction on test data. var metrics = context.MulticlassClassification.Evaluate(predictions); Console.WriteLine("Model's evaluation metrics:"); Console.WriteLine($" MicroAccuracy: {metrics.MicroAccuracy:0.###}"); Console.WriteLine($" MacroAccuracy: {metrics.MacroAccuracy:0.###}"); Console.WriteLine($" LogLoss: {metrics.LogLoss:#.###}"); Console.WriteLine($" LogLossReduction: {metrics.LogLossReduction:#.###}"); Console.WriteLine(); #endregion // Use predict engine to predict single values. var predictionEngine = context.Model.CreatePredictionEngine <Digit, DigitPrediction>(_model); Digit digit; DigitPrediction prediction; // Recognize image from img.bmp file. Console.WriteLine("Recognizing image from img.bmp file..."); digit = new Digit(_recognizeFile); digit.Draw(); prediction = predictionEngine.Predict(digit); Console.WriteLine($"Predicted number: {prediction.PredictedNumber}"); for (var i = 0; i < prediction.Score.Length; i++) { Console.WriteLine($"[{i}]: {prediction.Score[i].ToString("P2")}"); } Console.WriteLine(""); // Recognize random digit from test data set. Console.WriteLine("Recognizing random image from test data set..."); var rand = new Random(); digit = _testDigits.Skip(rand.Next(_testDigits.Length)).First(); digit.Draw(); prediction = predictionEngine.Predict(digit); Console.WriteLine($"Original number: {digit.Number}, predicted number: {prediction.PredictedNumber}"); for (var i = 0; i < prediction.Score.Length; i++) { Console.WriteLine($"[{i}]: {prediction.Score[i].ToString("P2")}"); } Console.WriteLine(""); }