|
| 1 | +# MNIST Classification |
| 2 | + |
| 3 | +| ML.NET version | API type | Status | App Type | Data type | Scenario | ML Task | Algorithms | |
| 4 | +|----------------|-------------------|-------------------------------|-------------|-----------|---------------------|---------------------------|-----------------------------| |
| 5 | +| v1.0.0-preview | Dynamic API | Up-to-date | Console app | .csv files | MNIST classification | Multi-class classification | Sdca Multi-class | |
| 6 | + |
| 7 | +In this introductory sample, you'll see how to use [ML.NET](https://www.microsoft.com/net/learn/apps/machine-learning-and-ai/ml-dotnet) to classify handwritten digits from 0 to 9 using the MNIST dataset. This is a **multiclass classification** problem that we will solve using SDCA (Stochastic Dual Coordinate Ascent) algorithm. |
| 8 | + |
| 9 | +## Problem |
| 10 | + |
| 11 | +The MNIST data set contains handwritten images of digits, ranging from 0 to 9. |
| 12 | + |
| 13 | +The MNIST dataset we are using contains 65 columns of numbers. The first 64 columns in each row are integer values in the range from 0 to 16. These values are calculated by dividing 32 x 32 bitmaps into non-overlapping blocks of 4 x 4. The number of ON pixels is counted in each of these blocks, which generates an input matrix of 8 x 8. The last column in each row is the number that is represented by the values in the first 64 columns. These first 64 columns are our features and our ML model will use these features to classifiy the testing images. The last column in our training and validation datasets is the label - the actual number that we will predict using our ML model. |
| 14 | + |
| 15 | +the ML model that we will build will return probabilities for a given image of being one of the numbers from 0 to 9 as explained above. |
| 16 | + |
| 17 | +## ML task - Multiclass classification |
| 18 | +The generalized problem of **multiclass classification** is to classify items into one of three or more classes. (Classifying items into one of the two classes is called **binary classification**). |
| 19 | + |
| 20 | +## Solution |
| 21 | +To solve this problem, first we will build an ML model. Then we will train the model on existing data, evaluate how good it is, and lastly we'll consume the model to predict a number the given image represents. |
| 22 | + |
| 23 | + |
| 24 | + |
| 25 | +### 1. Build model |
| 26 | + |
| 27 | +Building a model includes: |
| 28 | +* Uploading data (`optdigits-train.csv`) with (`DataReader`) |
| 29 | +* Create an Estimator and transform the data in the first 64 columns to one column so it can be used effectively by an ML algorithm (with `Concatenate`) |
| 30 | +* Choosing a learning algorithm (`StochasticDualCoordinateAscent`). |
| 31 | + |
| 32 | + |
| 33 | +The initial code is similar to the following: |
| 34 | +```fsharp |
| 35 | +// STEP 1: Common data loading configuration |
| 36 | +let trainData = mlContext.Data.LoadFromTextFile<Input>(trainDataPath, separatorChar=',', hasHeader=false) |
| 37 | +let testData = mlContext.Data.LoadFromTextFile<Input>(testDataPath, separatorChar=',', hasHeader=false) |
| 38 | +
|
| 39 | +// STEP 2: Common data process configuration with pipeline data transformations |
| 40 | +// Use in-memory cache for small/medium datasets to lower training time. Do NOT use it (remove .AppendCacheCheckpoint()) when handling very large datasets. |
| 41 | +let dataProcessPipeline = |
| 42 | + EstimatorChain() |
| 43 | + .Append(mlContext.Transforms.Conversion.MapValueToKey("Label", "Number")) |
| 44 | + .Append(mlContext.Transforms.Concatenate("Features", "PixelValues")) |
| 45 | + .AppendCacheCheckpoint(mlContext) |
| 46 | +
|
| 47 | +// STEP 3: Set the training algorithm, then create and config the modelBuilder |
| 48 | +let trainer = mlContext.MulticlassClassification.Trainers.SdcaMaximumEntropy("Label", "Features") |
| 49 | +let trainingPipeline = |
| 50 | + dataProcessPipeline |
| 51 | + .Append(trainer) |
| 52 | + .Append(mlContext.Transforms.Conversion.MapKeyToValue("Number", "Label")) |
| 53 | +``` |
| 54 | + |
| 55 | +### 2. Train model |
| 56 | +Training the model is a process of running the chosen algorithm on a training data to tune the parameters of the model. Our training data consists of pixel values and the digit they represent. It is implemented in the `Fit()` method from the Estimator object. |
| 57 | + |
| 58 | +To perform training we just call the method providing the training dataset (optdigits-train.csv file) in a DataView object. |
| 59 | + |
| 60 | +```fsharp |
| 61 | +// STEP 4: Train the model fitting to the DataSet |
| 62 | +let watch = System.Diagnostics.Stopwatch.StartNew(); |
| 63 | +printfn "=============== Training the model ===============" |
| 64 | +let trainedModel = trainingPipeline.Fit(trainData) |
| 65 | +``` |
| 66 | +### 3. Evaluate model |
| 67 | +We need this step to conclude how accurate our model operates on new data. To do so, the model from the previous step is run against another dataset that was not used in training (`optdigits-val.csv`). `MulticlassClassification.Evaluate` calculates the difference between known types and values predicted by the model in various metrics. |
| 68 | + |
| 69 | +```fsharp |
| 70 | +let predictions = trainedModel.Transform(testData) |
| 71 | +let metrics = mlContext.MulticlassClassification.Evaluate(predictions, "Number", "Score") |
| 72 | +
|
| 73 | +Common.ConsoleHelper.printMultiClassClassificationMetrics (trainer.ToString()) metrics |
| 74 | +``` |
| 75 | + |
| 76 | +>*To learn more on how to understand the metrics, check out the Machine Learning glossary from the [ML.NET Guide](https://docs.microsoft.com/en-us/dotnet/machine-learning/) or use any available materials on data science and machine learning*. |
| 77 | +
|
| 78 | +If you are not satisfied with the quality of the model, there are a variety of ways to improve it, which will be covered in the *examples* category. |
| 79 | + |
| 80 | +### 4. Consume model |
| 81 | +After the model is trained, we can use the `Predict()` API to predict the probability of being correct digit. |
| 82 | + |
| 83 | +```fsharp |
| 84 | +let loadedTrainedModel, modelInputSchema = mlContext.Model.Load modelPath |
| 85 | +
|
| 86 | +// Create prediction engine related to the loaded trained model |
| 87 | +let predEngine = mlContext.Model.CreatePredictionEngine<Input, Output>(loadedTrainedModel) |
| 88 | + |
| 89 | +sampleData |
| 90 | +|> Array.iter |
| 91 | + (fun (n,dat) -> |
| 92 | + let p = predEngine.Predict dat |
| 93 | + printfn "Actual: %d Predicted probability: zero: %.4f" n p.Score.[0] |
| 94 | + ["one:"; "two:"; "three:"; "four:"; "five:"; "six:"; "seven:"; "eight:"; "nine:"] |
| 95 | + |> List.iteri |
| 96 | + (fun i w -> |
| 97 | + let i = i + 1 |
| 98 | + printfn " %-6s %.4f" w p.Score.[i] |
| 99 | + ) |
| 100 | + printfn "" |
| 101 | + ) |
| 102 | +``` |
| 103 | + |
| 104 | +Where `sampleData` stores the pixel values of the digit that want to predict using the ML model. |
| 105 | + |
| 106 | +```fsharp |
| 107 | +let sampleData = |
| 108 | + [| |
| 109 | + 7, { |
| 110 | + Number = 0.f |
| 111 | + PixelValues = [|0.f;0.f;0.f;0.f;14.f;13.f;1.f;0.f;0.f;0.f;0.f;5.f;16.f;16.f;2.f;0.f;0.f;0.f;0.f;14.f;16.f;12.f;0.f;0.f;0.f;1.f;10.f;16.f;16.f;12.f;0.f;0.f;0.f;3.f;12.f;14.f;16.f;9.f;0.f;0.f;0.f;0.f;0.f;5.f;16.f;15.f;0.f;0.f;0.f;0.f;0.f;4.f;16.f;14.f;0.f;0.f;0.f;0.f;0.f;1.f;13.f;16.f;1.f;0.f|] |
| 112 | + } |
| 113 | + //... |
| 114 | + |] |
| 115 | +``` |
0 commit comments