Intermediate

Building Apps with Machine Learning in .NET

Add data and image classification to a Blazor app with ML.NET and Model Builder.

Main project: SmartBrew.WebApp (Blazor) Primary NuGet: Microsoft.ML Official site: dot.net/ml


Table of Contents

  1. Module 1 — Understanding Machine Learning and ML.NET
  2. Module 2 — Using Data Classification
  3. Module 3 — Working with Image Classification
  4. ML.NET In-Depth Technical Reference
  5. Model Builder Generated Code — Detailed Analysis
  6. Key Concepts and Quick Reference
  7. Overall Project Architecture

Module 1 — Understanding Machine Learning and ML.NET


What is Machine Learning?

Machine learning (ML) is a branch of artificial intelligence (AI). It allows machines to learn and perform tasks autonomously. In other words, machine learning allows us to program the unprogrammable.

Problems Difficult to Solve with Traditional Programming

With traditional programming (if/else, switch, while/foreach loops), certain questions are very difficult to handle:

QuestionML Problem Type
Is there a coffee cup in this image?Image classification
What language is this text written in?Data / text classification
Is this review negative?Binary classification (sentiment)
What should this product’s price be?Regression
Does this X-ray show a fracture?Medical image classification
Is this bank transaction fraudulent?Anomaly detection
Which product should we recommend to this user?Recommendation

Key concept: With machine learning, you provide data and an algorithm — the machine learns the rules. In contrast to traditional programming where you write the rules manually.

The Machine Learning Workflow

flowchart LR
    A[Define the problem] --> B[Prepare the data]
    B --> C[Choose a training\nalgorithm]
    C --> D[Tune the parameters\nhyperparameter tuning]
    D --> E[Evaluate the model]
    E -->|Model insufficient| C
    E -->|Model satisfactory| F[Deploy and use]
  1. Define the problem: What is the prediction objective?
  2. Prepare the data: Collect and clean the training data.
  3. Choose a training algorithm: Select from many available algorithms.
  4. Tune parameters (hyperparameter tuning): Optimize the algorithm.
  5. Evaluate the model: Measure performance on unseen data (validation data).

Understanding ML.NET

ML.NET is an open-source machine learning framework for .NET developers.

Key Characteristics

CharacteristicDetail
NuGet packageMicrosoft.ML
ExecutionOffline (cloud and on-premises)
Cross-platformWindows, Linux, macOS (anywhere .NET runs)
Architectures64-bit (all platforms), 32-bit Windows (except TensorFlow/ONNX/LightGBM)
Official sitedot.net/ml
Pre-trained modelsONNX and TensorFlow support in addition to custom models

Basic ML.NET Workflow in Code

// 1. Create the ML context (central entry point)
var mlContext = new MLContext(seed: 42);

// 2. Load training data into an IDataView
IDataView trainingData = mlContext.Data.LoadFromTextFile<ModelInput>(
    path: "languageDetection.csv",
    hasHeader: true,
    separatorChar: ',');

// 3. Build the processing and training pipeline
var pipeline = mlContext.Transforms.Text.FeaturizeText("Features", "Text")
    .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(
        labelColumnName: "Language",
        featureColumnName: "Features"))
    .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

// 4. Train the model (call Fit())
ITransformer model = pipeline.Fit(trainingData);

// 5. Use the model to make predictions
var predictionEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(model);
var prediction = predictionEngine.Predict(new ModelInput { Text = "I love coffee!" });
Console.WriteLine(prediction.PredictedLabel); // "English"

Note: Without AutoML or Model Builder, choosing the right training algorithm requires machine learning expertise. For example, for binary classification (classifying data into two categories: 0 or 1), ML.NET offers many different algorithms.

Full Example — Linear Regression (House Prices)

using Microsoft.ML;
using Microsoft.ML.Data;

// Data models
public record HouseData
{
    public float SquareFootage { get; set; }
    public float Price { get; set; }
}

public record Prediction
{
    [ColumnName("Score")]
    public float Price { get; set; }
}

// Program
var mlContext = new MLContext();

// Training data
HouseData[] houseData = [
    new() { SquareFootage = 1.1F, Price = 1.2F },
    new() { SquareFootage = 1.9F, Price = 2.3F },
    new() { SquareFootage = 2.8F, Price = 3.0F },
    new() { SquareFootage = 3.4F, Price = 3.7F }
];

IDataView trainingData = mlContext.Data.LoadFromEnumerable(houseData);

// Pipeline: Concatenate features + SDCA trainer
var pipeline = mlContext.Transforms.Concatenate("Features", ["SquareFootage"])
    .Append(mlContext.Regression.Trainers.Sdca(
        labelColumnName: "Price",
        maximumNumberOfIterations: 100));

// Training
var model = pipeline.Fit(trainingData);

// Prediction
var predEngine = mlContext.Model.CreatePredictionEngine<HouseData, Prediction>(model);
var price = predEngine.Predict(new HouseData { SquareFootage = 2.5F });
Console.WriteLine($"Predicted price: {price.Price * 100:C}k"); // ~261.98k

// Evaluation
var metrics = mlContext.Regression.Evaluate(model.Transform(trainingData), labelColumnName: "Price");
Console.WriteLine($"R2: {metrics.RSquared:0.##}");
Console.WriteLine($"RMS error: {metrics.RootMeanSquaredError:0.##}");

// Save
mlContext.Model.Save(model, trainingData.Schema, "model.zip");

ML.NET Internal Architecture

MLContext — Central Catalog

The MLContext object is the singleton entry point for ML.NET. It exposes catalogs which are component factories:

CatalogC# TypeRole
DataDataOperationsCatalogLoading and saving data
TransformsTransformsCatalogData preparation and transformation
BinaryClassificationBinaryClassificationCatalogBinary classification tasks
MulticlassClassificationMulticlassClassificationCatalogMulticlass classification tasks
RegressionRegressionCatalogRegression tasks
AnomalyDetectionAnomalyDetectionCatalogAnomaly detection
ClusteringClusteringCatalogData clustering
ForecastingForecastingCatalogTime series forecasting
RankingRankingCatalogRanking
RecommendationRecommendationCatalogRecommendation systems
ModelModelOperationsCatalogCreating PredictionEngine, saving/loading
graph TD
    CTX["MLContext\n(singleton)"]
    CTX --> DATA["Data\n(DataOperationsCatalog)"]
    CTX --> TRANS["Transforms\n(TransformsCatalog)"]
    CTX --> BIN["BinaryClassification"]
    CTX --> MULTI["MulticlassClassification"]
    CTX --> REG["Regression"]
    CTX --> ANOM["AnomalyDetection"]
    CTX --> CLUST["Clustering"]
    CTX --> MOD["Model\n(save/load/predict)"]

IDataView — Lazy Data View

IDataView is the central interface for representing data in ML.NET. It is lazily evaluated: data is only loaded and processed when needed.

// Load from a CSV file
IDataView dataView = mlContext.Data.LoadFromTextFile<ModelInput>(
    path: "data.csv",
    hasHeader: true,
    separatorChar: ',');

// Load from an in-memory collection
IDataView dataView2 = mlContext.Data.LoadFromEnumerable(myList);

// Preview for debugging (DO NOT use in production)
var debug = dataView.Preview();

IDataView characteristics:

  • Named columns with types and lengths
  • Lazy evaluation: loading only happens during Fit() or Transform()
  • All ML.NET algorithms expect a vector column named Features

Pipeline: IEstimator and ITransformer

flowchart LR
    EST["IEstimator\n(before training)\nDefines operations"]
    IDATAVIEW["IDataView\n(training data)"]
    FIT["pipeline.Fit(data)"]
    TRANS["ITransformer\n(after training)\nContains learned parameters"]

    EST --> FIT
    IDATAVIEW --> FIT
    FIT --> TRANS
    TRANS -->|"Transform(data)"| PRED["IDataView\n(predictions)"]
InterfaceTimingDescription
IEstimator<T>Before trainingDefines the pipeline operations
ITransformerAfter trainingThe trained model with its parameters
// pipeline is an IEstimator
var pipeline = mlContext.Transforms.Concatenate("Features", ["SquareFootage"])
    .Append(mlContext.Regression.Trainers.Sdca(labelColumnName: "Price"));

// model is an ITransformer (after Fit)
ITransformer model = pipeline.Fit(trainingData);

// Use the transformer to make bulk predictions
IDataView predictions = model.Transform(inputData);

AutoML and Model Builder

AutoML

AutoML (Automated Machine Learning) automates the complex steps of the ML workflow:

flowchart LR
    DEV[".NET Developer\n(you)"] -->|Defines| P["Problem"]
    DEV -->|Prepares| D["Data"]
    P --> AUTO["AutoML\n(Microsoft.ML.AutoML)"]
    D --> AUTO
    AUTO -->|Automatically chooses| A["Training algorithm"]
    AUTO -->|Automatically tunes| H["Hyperparameters"]
    AUTO -->|Evaluates and selects| M["Best final model"]
StepResponsible
Define the problemYou
Prepare the dataYou
Choose an algorithmAutoML
Tune parametersAutoML
Evaluate the modelAutoML

NuGet: Microsoft.ML.AutoML (depends on Microsoft.ML)

Model Builder

Model Builder is a Visual Studio extension built on AutoML and ML.NET.

graph TD
    MB["Model Builder\n(Visual Studio Extension)"]
    AUTO["AutoML\n(Microsoft.ML.AutoML)"]
    MLNET["ML.NET\n(Microsoft.ML)"]
    MB --> AUTO
    AUTO --> MLNET

Model Builder Workflow in Visual Studio

flowchart LR
    S1["1. Scenario\n(what type of ML?)"] --> S2["2. Environment\n(CPU / GPU / Azure)"]
    S2 --> S3["3. Data\n(CSV / SQL / Images)"]
    S3 --> S4["4. Training\n(AutoML explores)"]
    S4 --> S5["5. Evaluation\n(test the model)"]
    S5 --> S6["6. Consumption\n(generated C# code)"]

Environment Setup

Prerequisites

  • Visual Studio 2022 (Community, Professional, or Enterprise)
    • Workload: ASP.NET and web development
    • Extension: ML.NET Model Builder 2022

Verifying the Installation

  1. Open Visual Studio Installer
  2. Click Modify on Visual Studio 2022
  3. Workloads tab → verify ASP.NET and web development
  4. Individual components tab → search ML.NET → verify ML.NET Model Builder 2022

Warning: There are two extensions — VS 2019 (without the “2022” suffix) and VS 2022. Make sure to have the 2022 version.


Exploring the Starter Project

The SmartBrew.WebApp project is a Blazor application representing the fictional company Smart Brew Coffee.

Application Structure

SmartBrew.WebApp/
+-- Components/
|   +-- Pages/
|       +-- LanguageDetection.razor   <- Scenario 1: Data classification
|       +-- BeverageDetection.razor   <- Scenario 2: Image classification
+-- (other Blazor files)

Two ML Scenarios to Implement

ScenarioML TypeGoal
Language detectionData classificationDetect the language of entered text
Beverage detectionImage classificationDetect whether an image contains a coffee beverage

Difference: Data Classification vs Text Classification

Data classificationText classification
DataTabular data (numbers + text)Raw text only
ExampleDetect a language, classify GitHub issuesSentiment analysis
FlexibilityMore flexibleText-specialized

Module 2 — Using Data Classification


Understanding the Task: Language Detection

The goal is to automatically detect the language of input text. Target languages: English, French, German, Italian (then Spanish after retraining).

Training Data

The file languageDetection.csv contains two columns:

Language,Message
German,"Ich lerne gerne Deutsch"
French,"J aime apprendre le francais"
Italian,"Mi piace imparare l italiano"
English,"I like learning new languages"
German,"Ich liebe Kaffee"
French,"J aime le cafe"
Italian,"Mi piace il caffe"
English,"I love coffee"
ColumnML RoleDescription
LanguageLabelValue to predict
MessageFeatureData used for the prediction

ML advantage: To add new languages, simply retrain the model with new data — no need to modify the application code.


Creating a Model Builder Configuration File

Steps in Visual Studio

  1. Right-click the project in Solution Explorer
  2. Add → Machine Learning Model…
  3. Select Machine Learning Model (ML.NET) type
  4. Name it: LanguageDetectionModelAdd

Generated File: .mbconfig

The .mbconfig file is a JSON file:

{
  "Scenario": "Classification",
  "DataSource": {
    "Type": "TabularFile",
    "FilePath": "languageDetection.csv",
    "Delimiter": ",",
    "DecimalMarker": ".",
    "HasHeader": true,
    "ColumnProperties": [
      { "ColumnName": "Language", "Purpose": "Label", "Type": "String" },
      { "ColumnName": "Message",  "Purpose": "Feature", "Type": "String" }
    ]
  },
  "Environment": { "Type": "LocalCPU" },
  "MLConfig": {
    "TrainingTime": 20,
    "Scenario": { "Type": "Classification", "LabelColumnName": "Language" }
  }
}

Selecting a Scenario

Scenario Types in Model Builder

CategoryScenariosStatusEnvironments
TabularData classificationStableLocal CPU
TabularValue prediction (regression)StableLocal CPU
TabularRecommendationStableLocal CPU
Computer VisionImage classificationStableCPU / GPU / Azure
Computer VisionObject detectionPreviewCPU / Azure
NLPText classificationPreviewCPU / Azure

For Language Detection

  • Selected scenario: Data classification
  • Environment: Local (CPU) — the only available option for this scenario

Specifying Training Data

Supported Data Sources

flowchart LR
    DS["Data Source\nModel Builder"] --> F["CSV / TSV File\n(TabularFile)"]
    DS --> SQL["SQL Server Table\n(SqlServerTable)"]

Column Settings — Purposes

PurposeDescriptionExample
LabelColumn to predict — only one per datasetLanguage
FeatureColumn used for predictionMessage
IgnoreColumn not usedRowId

Default split: 80% training / 20% validation


Training the Model

Training Parameters

ParameterValue
ScenarioData classification
EnvironmentLocal (CPU)
DatalanguageDetection.csv
Training time20 seconds

AutoML Process

flowchart TD
    START["Click: Start Training"] --> EXPLORE["AutoML explores different\ntraining algorithms"]
    EXPLORE --> EVAL["Evaluation of each algorithm\n(accuracy, F1-score, macro accuracy...)"]
    EVAL --> BEST["Best model selected\ne.g.: LbfgsMaximumEntropyMulti"]
    BEST --> GEN["Generated:\n- model.mlnet (binary)\n- .consumption.cs\n- .training.cs"]

Example result: 69 models explored in 18.9 seconds → best model: LbfgsMaximumEntropyMulti

Files Generated After Training

SmartBrew.WebApp/
+-- LanguageDetectionModel.mbconfig
    +-- LanguageDetectionModel.mlnet            <- Trained model (binary, zip format)
    +-- LanguageDetectionModel.training.cs      <- C# code for retraining
    +-- LanguageDetectionModel.consumption.cs   <- C# code for using the model

Advanced Training Options

OptionDescription
Optimization metricMeasure used to select the best model (e.g., Macro Accuracy)
Include/exclude algorithmsExclude poorly-performing algorithms
AutoML hyperparameter tunersSMAC, CostFrugal… configuration
Stop/sample strategiesControl of exploration stopping

Evaluating the Model

Test messagePredicted languageProbability
”I like learning new languages”English86%
“Ich lerne gerne Deutsch”German90%
“J aime apprendre le francais”French56%

If the model doesn’t perform well:

  1. Increase the training time
  2. In Advanced training options → Trainers, exclude poorly-performing algorithms
  3. Add more training data (more CSV rows)

Consuming the Model in the Application

Code Generated by Model Builder

// LanguageDetection.razor — @code block

private string? detectedLanguage;

private void TextChanged(string? text)
{
    if (string.IsNullOrEmpty(text))
    {
        detectedLanguage = null;
        return;
    }

    // 1. Create the input object (the features)
    var input = new LanguageDetectionModel.ModelInput()
    {
        Message = text,
    };

    // 2. Call Predict() — returns the best label
    var output = LanguageDetectionModel.Predict(input);

    // 3. Retrieve the predicted label
    detectedLanguage = output.PredictedLabel;
}

Full Data Flow — Data Classification

sequenceDiagram
    participant U as User
    participant B as Blazor Component
    participant M as LanguageDetectionModel

    U->>B: Types text in the TextArea
    B->>B: TextChanged(text) fires
    B->>B: new ModelInput { Message = text }
    B->>M: LanguageDetectionModel.Predict(input)
    note over M: Internal pipeline:\n1. FeaturizeText\n2. LbfgsMaximumEntropy\n3. MapKeyToValue
    M-->>B: ModelOutput { PredictedLabel: "English" }
    B->>U: Displays "Detected language: English"

Predicting All Labels

// LanguageDetection.razor — complete @code block

private string? detectedLanguage;
private IOrderedEnumerable<KeyValuePair<string, float>>? allLabels;

private void TextChanged(string? text)
{
    if (string.IsNullOrEmpty(text))
    {
        detectedLanguage = null;
        allLabels = null;
        return;
    }

    var input = new LanguageDetectionModel.ModelInput { Message = text };

    // Predict the best label
    var output = LanguageDetectionModel.Predict(input);
    detectedLanguage = output.PredictedLabel;

    // All labels with their scores (sorted descending)
    allLabels = LanguageDetectionModel.PredictAllLabels(input);
}

Blazor Template

<div>Detected language: @detectedLanguage</div>

@if (allLabels != null)
{
    <ul>
        @foreach (var label in allLabels)
        {
            <li>@label.Key : @(label.Value.ToString("P0"))</li>
        }
    </ul>
}

Note: float between 0 and 1. PredictAllLabels() returns IOrderedEnumerable<KeyValuePair<string, float>> sorted by descending score.


Retraining the Model

To add new languages (e.g., Spanish), simply modify the training data and retrain — without changing the application code.

Retraining Process

flowchart LR
    A["Add rows\n(Spanish in CSV)"] --> B["Save the CSV"]
    B --> C["Model Builder -> Train tab"]
    C --> D["Click Train again"]
    D --> E["AutoML re-explores\nthe algorithms"]
    E --> F["New model\ne.g.: SdcaMaximumEntropyMulti"]
    F --> G[".mlnet and .cs files\nautomatically updated"]

Example Spanish Data Added

Spanish,"Me gusta aprender idiomas nuevos"
Spanish,"Hola, como estas?"
Spanish,"Bienvenido a Smart Brew Coffee"
Spanish,"El cafe es delicioso"
Spanish,"Me encanta el machine learning"
Spanish,"Estoy aprendiendo con esta plataforma"

Result After Retraining

BeforeAfter
LbfgsMaximumEntropyMultiSdcaMaximumEntropyMulti
4 languages (EN/FR/DE/IT)5 languages + Spanish
C# code unchangedC# code unchanged

Module 3 — Working with Image Classification


Understanding the Task: Beverage Detection

The goal is to automatically detect the content of an image across three categories: coffee, goat, other.

Training Data

Images/
+-- coffee/   <- 15 coffee images
+-- goat/     <- 6 goat images
+-- other/    <- 7 images of other subjects

Selecting the Scenario and Configuring the GPU

Available Environments for Image Classification

EnvironmentDescriptionPerformance
Local (CPU)Standard processorSlow
Local (GPU)NVIDIA GPU with CUDAFast
AzureMicrosoft cloudVariable

GPU Configuration (NVIDIA CUDA)

graph TD
    GPU["NVIDIA GPU\n(CUDA-compatible)"]
    CUDA["CUDA 10.1\nCompute Unified Device Architecture"]
    CUDNN["cuDNN 7.6.4\nCUDA Deep Neural Network library"]
    MLVISION["Microsoft.ML.Vision\n(NuGet — auto-installed)"]

    GPU --> CUDA
    CUDA --> CUDNN
    CUDNN --> MLVISION
ComponentRequired VersionRole
CUDA10.1 (exactly)Framework for GPU access
cuDNN7.6.4 (exactly)Deep neural networks library
Microsoft.ML.VisionAuto-installedML.NET NuGet for vision

CUDA = framework allowing developers to access the raw computing power of the GPU cuDNN = library built on CUDA providing primitives for deep neural networks


Specifying Image Training Data

Required Folder Structure

Model Builder expects each subfolder to be a label for the images it contains:

Images/                <- root folder selected
+-- coffee/            <- label "coffee" (15 images)
+-- goat/              <- label "goat" (6 images)
+-- other/             <- label "other" (7 images)

Data Split

ParameterDefault value
Training data80% of images
Validation data20% of images

Training the Image Model

Configuration Summary

ParameterValue
ScenarioImage classification
EnvironmentLocal (GPU)
DataImages/ folder

Automatically Installed NuGet Packages

<PackageReference Include="Microsoft.ML" />
<PackageReference Include="Microsoft.ML.Vision" />
<PackageReference Include="Microsoft.ML.ImageAnalytics" />

Generated Files

SmartBrew.WebApp/
+-- BeverageDetectionModel.mbconfig
    +-- BeverageDetectionModel.mlnet              <- Trained model (binary)
    +-- BeverageDetectionModel.training.cs        <- Retraining code
    +-- BeverageDetectionModel.consumption.cs     <- Consumption code

Difference: For Image classification, Model Builder does not generate a separate evaluation file.


Evaluating the Image Model

Tested imagePredictionProbability
Coffee image (training data)coffee100%
Person drinking a coffeecoffee97%
Goat image (training data)goat97%
Smart Brew Coffee logo (cup)coffee82%
Portrait photoother62%

Recommendation: Test the model with images not present in the training data to get a realistic evaluation.


Consuming the Image Model in the Application

Implementation in BeverageDetection.razor

// BeverageDetection.razor — @code block

private string detectedContent = "None";

private async Task LoadFileAsync(InputFileChangeEventArgs e)
{
    // 1. Read image file -> byte[]
    var file = e.File;
    using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
    using var memoryStream = new MemoryStream();
    await stream.CopyToAsync(memoryStream);
    byte[] imageData = memoryStream.ToArray();

    // 2. Create model input (ImageSource = byte[])
    var input = new BeverageDetectionModel.ModelInput()
    {
        ImageSource = imageData,
    };

    // 3. Call Predict()
    var output = BeverageDetectionModel.Predict(input);

    // 4. Assign the predicted label
    detectedContent = output.PredictedLabel; // "coffee", "goat", or "other"
}

ModelInput / ModelOutput Classes for Image Classification

public class ModelInput
{
    // Image as a byte array
    public byte[] ImageSource { get; set; }
}

public class ModelOutput
{
    [ColumnName("PredictedLabel")]
    public string PredictedLabel { get; set; }

    // Scores for each label (between 0 and 1, sum = 1)
    public float[] Score { get; set; }
}

Data Flow — Image Classification

sequenceDiagram
    participant U as User
    participant B as BeverageDetection.razor
    participant M as BeverageDetectionModel

    U->>B: Selects an image
    B->>B: file.OpenReadStream() -> byte[]
    B->>B: new ModelInput { ImageSource = bytes }
    B->>M: BeverageDetectionModel.Predict(input)
    note over M: Pipeline:\n1. LoadRawImageBytes\n2. ResizeImages\n3. ExtractPixels\n4. DnnFeaturizeImage\n5. Softmax -> PredictedLabel
    M-->>B: ModelOutput { PredictedLabel: "coffee" }
    B->>U: Displays "Detected content: coffee"

Predicting All Image Labels

// BeverageDetection.razor — enriched @code block

private string detectedContent = "None";
private IOrderedEnumerable<KeyValuePair<string, float>>? allLabels;

private async Task LoadFileAsync(InputFileChangeEventArgs e)
{
    using var ms = new MemoryStream();
    await e.File.OpenReadStream(10 * 1024 * 1024).CopyToAsync(ms);
    byte[] imageData = ms.ToArray();

    var input = new BeverageDetectionModel.ModelInput { ImageSource = imageData };

    // Predict the best label
    var output = BeverageDetectionModel.Predict(input);
    detectedContent = output.PredictedLabel;

    // All labels with their scores
    allLabels = BeverageDetectionModel.PredictAllLabels(input);
}

Blazor Template

<p>Detected content: <strong>@detectedContent</strong></p>

@if (allLabels != null)
{
    <ul>
        @foreach (var label in allLabels)
        {
            <li>@label.Key : @(label.Value.ToString("P0"))</li>
        }
    </ul>
}

Example output:

Detected content: coffee
- coffee : 98%
- other  : 1%
- goat   : 1%

ML.NET In-Depth Technical Reference


IDataView — Data View

// Load from a CSV file
IDataView dataView = mlContext.Data.LoadFromTextFile<ModelInput>(
    path: "data.csv", hasHeader: true, separatorChar: ',');

// Load from a collection
IDataView dataView2 = mlContext.Data.LoadFromEnumerable(myList);

// Save
using var fileStream = File.Create("output.csv");
mlContext.Data.SaveAsText(dataView, fileStream, separatorChar: ',');

// Preview for debugging ONLY (degrades performance in production)
var preview = dataView.Preview(maxRows: 10);

IDataView properties:

  • Lazy evaluation: data is read and processed only during Fit() or Transform()
  • Schema: each column has a name, type, and size
  • All ML.NET algorithms expect a vector column named Features
  • Algorithms create output columns with fixed names (Score, PredictedLabel)

Pipeline: IEstimator and ITransformer

// Building the pipeline (no execution at this point)
var pipeline = mlContext.Transforms.Text.FeaturizeText(
        outputColumnName: "Features",
        inputColumnName: "Message")
    .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(
        labelColumnName: "Language",
        featureColumnName: "Features"))
    .Append(mlContext.Transforms.Conversion.MapKeyToValue(
        outputColumnName: "PredictedLabel"));

// Training -> ITransformer
ITransformer model = pipeline.Fit(trainingData);

// Bulk transformation (batch predictions)
IDataView predictions = model.Transform(testData);

// Multiclass evaluation
var metrics = mlContext.MulticlassClassification.Evaluate(
    predictions,
    labelColumnName: "Language",
    predictedLabelColumnName: "PredictedLabel");

Console.WriteLine($"Macro Accuracy: {metrics.MacroAccuracy:P2}");
Console.WriteLine($"Micro Accuracy: {metrics.MicroAccuracy:P2}");
Console.WriteLine($"Log-loss:       {metrics.LogLoss:F4}");

PredictionEngine vs PredictionEnginePool

PredictionEngine<TInput, TOutput> is not thread-safe. For web applications, use PredictionEnginePool.

// Not thread-safe (OK for console, WinForms, Blazor WASM)
var predEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(model);
var result = predEngine.Predict(input);

// Thread-safe for ASP.NET Core / Blazor Server
// In Program.cs:
builder.Services.AddPredictionEnginePool<ModelInput, ModelOutput>()
    .FromFile(modelName: "LanguageModel", filePath: "model.zip", watchForChanges: true);

// In an injectable service:
public class LanguageService
{
    private readonly PredictionEnginePool<ModelInput, ModelOutput> _pool;

    public LanguageService(PredictionEnginePool<ModelInput, ModelOutput> pool)
        => _pool = pool;

    public string Predict(string message)
    {
        var input = new ModelInput { Message = message };
        var output = _pool.Predict(modelName: "LanguageModel", example: input);
        return output.PredictedLabel;
    }
}

In the SmartBrew.WebApp course: Blazor WebAssembly runs client-side, so the standard PredictionEngine is used without threading issues. In production with Blazor Server or ASP.NET Core, prefer PredictionEnginePool.


Available Transformations

Column Transformations

TransformationDescription
ConcatenateCombine multiple columns into a Features vector
CopyColumnsCopy and rename a column
DropColumnsRemove unnecessary columns
SelectColumnsKeep only certain columns

Normalization and Scaling

TransformationDescription
NormalizeMeanVarianceSubtracts the mean, divides by variance
NormalizeMinMaxNormalizes between 0 and 1
NormalizeLpNormL1, L2, or infinity norm
NormalizeRobustScalingRobust to outliers

Text Transformations

TransformationDescription
FeaturizeTextTransforms text -> float vector (n-grams + char-grams)
TokenizeIntoWordsSplit text into words
NormalizeTextNormalize case, punctuation, diacritics
RemoveDefaultStopWordsRemove stop words (the, le, der…)
ApplyWordEmbeddingConvert tokens into semantic vectors (Word2Vec…)
LatentDirichletAllocationTopic modeling

Image Transformations

TransformationDescription
LoadImagesLoad images from a folder
LoadRawImageBytesLoad an image from a byte[]
ResizeImagesResize images
ExtractPixelsConvert image -> number vector
ConvertToGrayscaleConvert to grayscale
DnnFeaturizeImageTransform image via pre-trained DNN

Categorical Transformations

TransformationDescription
OneHotEncodingEncode categories into binary vectors
MapValueToKeyMap string values to numeric keys
MapKeyToValueConvert keys back to original values

Deep Learning Transformations

TransformationDescription
ApplyOnnxModelApply an imported ONNX model
LoadTensorFlowModelApply an imported TensorFlow model

Training Algorithms (Trainers)

Binary Classification (mlContext.BinaryClassification.Trainers)

AlgorithmTypical Use
SdcaLogisticRegressionGeneral purpose
LbfgsLogisticRegressionSmall datasets
FastTreeTabular data (gradient boosting)
FastForestRandom forest — robust to noise
LinearSvmLarge datasets
AveragedPerceptronLinearly separable data

Multiclass Classification (mlContext.MulticlassClassification.Trainers)

AlgorithmDescriptionCourse result
LbfgsMaximumEntropyMaximum entropy with L-BFGSInitial training (4 languages)
SdcaMaximumEntropyMaximum entropy with SDCARetraining (5 languages)
NaiveBayesNaive Bayesian classifierQuick baseline
OneVersusAllOne-versus-all (wrapper)Extends binary classifiers

Regression (mlContext.Regression.Trainers)

AlgorithmDescription
SdcaStochastic Dual Coordinate Ascent
LbfgsPoissonRegressionPoisson regression with L-BFGS
FastTreeGradient boosted trees
LightGbmLight Gradient Boosting Machine

Saving and Loading a Model

// SAVE
mlContext.Model.Save(
    model: trainedModel,
    inputSchema: trainingData.Schema,
    filePath: "model.zip");

// LOAD
ITransformer loadedModel = mlContext.Model.Load(
    filePath: "model.zip",
    inputSchema: out DataViewSchema modelSchema);

// Recreate the PredictionEngine after loading
var predEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(loadedModel);

Note: .mlnet files are ZIP archives containing the serialized model. Save / Load use the same format.


ONNX Integration with ML.NET

ONNX (Open Neural Network Exchange) is an open format for machine learning models. ML.NET can import and use pre-trained ONNX models.

// Using a pre-trained ONNX model
var pipeline = mlContext.Transforms.ApplyOnnxModel(
    modelFile: "model.onnx",
    outputColumnNames: new[] { "output" },
    inputColumnNames: new[] { "input" });

// Example: YOLOv4 for object detection
var onnxPipeline = mlContext.Transforms
    .ResizeImages("input_image", 416, 416, "image")
    .Append(mlContext.Transforms.ExtractPixels("input", "input_image"))
    .Append(mlContext.Transforms.ApplyOnnxModel(
        modelFile: "yolov4.onnx",
        outputColumnNames: new[] { "Identity:0", "Identity_1:0", "Identity_2:0" },
        inputColumnNames: new[] { "input_1:0" }));

ONNX use cases:

  • Pre-trained models from PyTorch, scikit-learn, etc.
  • Object detection models (YOLO, SSD)
  • NLP models (BERT, GPT)
  • Reusing models trained in other frameworks

Constraint: Requires Microsoft.ML.OnnxTransformer. x64 only, not x86.


TensorFlow Integration with ML.NET

ML.NET can import TensorFlow SavedModel models for scoring and transfer learning.

// Loading a TensorFlow SavedModel
var pipeline = mlContext.Model.LoadTensorFlowModel("./tensorflow_model")
    .ScoreTensorFlowModel(
        outputColumnNames: new[] { "softmax2" },
        inputColumnNames: new[] { "input" },
        addBatchDimensionInput: true);

// Transfer Learning with TensorFlow Inception v3
var retrainedPipeline = mlContext.Model.LoadTensorFlowModel(inceptionV3ModelPath)
    .RetrainTensorFlowModel(
        inputTensorNames: new[] { "input" },
        outputTensorNames: new[] { "InceptionV3/Predictions/Reshape_1" },
        labelColumnName: "LabelAsKey",
        inputColumnName: "input",
        arch: ImageClassificationTrainer.Architecture.InceptionV3);

Constraint: Requires Microsoft.ML.TensorFlow. Not supported on x86.


Model Builder Generated Code — Detailed Analysis


.consumption.cs File

// LanguageDetectionModel.consumption.cs
// Code automatically generated by ML.NET Model Builder

using Microsoft.ML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public partial class LanguageDetectionModel
{
    private static string MLNetModelPath = Path.GetFullPath("LanguageDetectionModel.mlnet");

    // Lazy initialization of PredictionEngine
    public static readonly Lazy<PredictionEngine<ModelInput, ModelOutput>> PredictEngine =
        new Lazy<PredictionEngine<ModelInput, ModelOutput>>(
            () => CreatePredictEngine(), true);

    /// <summary>Input class — the model's features</summary>
    public class ModelInput
    {
        [LoadColumn(0), ColumnName("Language")]
        public string Language { get; set; }

        [LoadColumn(1), ColumnName("Message")]
        public string Message { get; set; }
    }

    /// <summary>Output class — the prediction</summary>
    public class ModelOutput
    {
        [ColumnName("PredictedLabel")]
        public string PredictedLabel { get; set; }

        public float[] Score { get; set; }
    }

    private static PredictionEngine<ModelInput, ModelOutput> CreatePredictEngine()
    {
        var mlContext = new MLContext();
        ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var _);
        return mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);
    }

    /// <summary>Predicts the best label</summary>
    public static ModelOutput Predict(ModelInput input)
        => PredictEngine.Value.Predict(input);

    /// <summary>Predicts all labels with their scores, sorted by descending probability</summary>
    public static IOrderedEnumerable<KeyValuePair<string, float>> PredictAllLabels(ModelInput input)
    {
        var predEngine = PredictEngine.Value;
        var result = predEngine.Predict(input);

        predEngine.OutputSchema[nameof(ModelOutput.Score)]
            .GetSlotNames<string>(out var slotNamesBuffer);

        var labelScores = slotNamesBuffer.DenseValues()
            .Select((name, index) => new KeyValuePair<string, float>(name, result.Score[index]));

        return labelScores.OrderByDescending(kv => kv.Value);
    }
}

.training.cs File

// LanguageDetectionModel.training.cs
// Code automatically generated by ML.NET Model Builder

using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers;

public partial class LanguageDetectionModel
{
    private const string TRAIN_DATA_FILEPATH = @"languageDetection.csv";
    private const int TRAINING_TIME = 20;

    /// <summary>Retrain the model with the current data</summary>
    public static ITransformer RetrainPipeline(MLContext mlContext, IDataView trainData)
    {
        var pipeline = BuildPipeline(mlContext);
        return pipeline.Fit(trainData);
    }

    /// <summary>Build the ML.NET pipeline</summary>
    public static IEstimator<ITransformer> BuildPipeline(MLContext mlContext)
    {
        var pipeline = mlContext.Transforms.Conversion
            .MapValueToKey(outputColumnName: "Language", inputColumnName: "Language")
            .Append(mlContext.Transforms.Text.FeaturizeText(
                outputColumnName: "Message_tf", inputColumnName: "Message"))
            .Append(mlContext.Transforms.CopyColumns(
                outputColumnName: "Features", inputColumnName: "Message_tf"))
            .Append(mlContext.Transforms.NormalizeMinMax(
                outputColumnName: "Features", inputColumnName: "Features"))
            .AppendCacheCheckpoint(mlContext)
            .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(
                new LbfgsMaximumEntropyMulticlassTrainer.Options
                {
                    L1Regularization = 0.03125F,
                    L2Regularization = 0.2158F,
                    LabelColumnName  = "Language",
                    FeatureColumnName = "Features"
                }))
            .Append(mlContext.Transforms.Conversion.MapKeyToValue(
                outputColumnName: "PredictedLabel",
                inputColumnName:  "PredictedLabel"));

        return pipeline;
    }
}

Standalone Retraining Program

// Console program to manually retrain the model
class RetrainingProgram
{
    static void Main(string[] args)
    {
        var mlContext = new MLContext(seed: 1);

        IDataView trainData = mlContext.Data.LoadFromTextFile<LanguageDetectionModel.ModelInput>(
            path: "languageDetection.csv",
            hasHeader: true,
            separatorChar: ',');

        Console.WriteLine("Starting training...");
        var model = LanguageDetectionModel.RetrainPipeline(mlContext, trainData);
        Console.WriteLine("Training complete.");

        // Evaluation
        var predictions = model.Transform(trainData);
        var metrics = mlContext.MulticlassClassification.Evaluate(
            predictions, labelColumnName: "Language");

        Console.WriteLine($"Macro Accuracy: {metrics.MacroAccuracy:P2}");
        Console.WriteLine($"Micro Accuracy: {metrics.MicroAccuracy:P2}");

        // Save
        mlContext.Model.Save(model, trainData.Schema, "LanguageDetectionModel.mlnet");
        Console.WriteLine("Model saved: LanguageDetectionModel.mlnet");
    }
}

Key Concepts and Quick Reference

Comparison of the Two Implemented Scenarios

Language DetectionBeverage Detection
Scenario typeData classificationImage classification
Model Builder configLanguageDetectionModel.mbconfigBeverageDetectionModel.mbconfig
Input dataCSV with text columnsImage folder with subfolders per label
Input typestring Messagebyte[] ImageSource
LabelsEnglish, French, German, Italian, Spanishcoffee, goat, other
EnvironmentLocal CPU onlyLocal CPU / GPU / Azure
Best algorithm (initial)LbfgsMaximumEntropyMultiDeep Neural Network (cuDNN)
Best algorithm (retrain)SdcaMaximumEntropyMulti
Additional NuGetMicrosoft.ML.Vision, Microsoft.ML.ImageAnalytics
Data splitN/A80% train / 20% validation

Universal ML.NET Model Consumption Pattern

// =======================================================
// UNIVERSAL PATTERN — ML.NET Model Consumption
// =======================================================

// 1. Define the INPUT (the features)
var input = new MyModel.ModelInput
{
    Message = "text to analyze",      // Data classification
    // OR
    ImageSource = imageByteArray,     // Image classification
};

// 2. PREDICT the best label
var output = MyModel.Predict(input);
string label   = output.PredictedLabel;  // e.g.: "English" or "coffee"
float[] scores = output.Score;           // array of scores 0-1

// 3. (Optional) ALL labels with their scores
IOrderedEnumerable<KeyValuePair<string, float>> allLabels =
    MyModel.PredictAllLabels(input);
// Key = label (string), Value = score (float 0-1), sorted desc

Available ML.NET Tasks

TaskDescriptionExample
Binary Classification2 categories (0/1)Spam/not spam
Multiclass Classification3+ categoriesLanguage detection
RegressionContinuous valueHouse price
Anomaly DetectionAbnormal valuesBank fraud
RecommendationRecommend itemsE-commerce
ClusteringGroup without labelsCustomer segmentation
Image ClassificationCategorize imagesCoffee/goat/other
Object DetectionLocate objectsBounding boxes
Time SeriesTemporal dataSales forecasting
Text ClassificationCategorize textNLP, sentiment

ML Glossary

TermDefinition
FeatureInput data used to make a prediction
LabelValue to predict (target column)
Training dataData used to train the model (80%)
Validation dataData used to validate performance (20%)
PipelineChain of transformations and training (IEstimator)
MLContextCentral entry point for ML.NET (singleton)
IDataViewLazy data view interface
IEstimatorPipeline interface before training
ITransformerModel interface after training (Fit)
PredictionEnginePrediction engine for individual examples (not thread-safe)
PredictionEnginePoolThread-safe prediction engine for web apps
Binary classificationClassification into 2 categories (0/1)
Multiclass classificationClassification into more than 2 categories
ScoreProbability associated with each label (float 0 to 1)
PredictedLabelThe label with the highest probability
AutoMLAutomated Machine Learning — automates algorithm selection
Hyperparameter tuningAdjustment of an ML algorithm’s parameters
mbconfigModel Builder configuration file (JSON)
mlnetBinary ZIP file containing the trained model
ONNXOpen Neural Network Exchange — interoperable format
CUDACompute Unified Device Architecture — NVIDIA GPU framework
cuDNNCUDA Deep Neural Network library
Transfer learningReuse a pre-trained model as a base
Fit()Pipeline training method
Transform()Method to apply the model to data
Predict()Prediction method for a single example

NuGet Packages

PackageRole
Microsoft.MLCore ML.NET framework
Microsoft.ML.AutoMLAutoML for automatic algorithm selection
Microsoft.ML.VisionImage classification
Microsoft.ML.ImageAnalyticsImage processing and transformation
Microsoft.ML.OnnxTransformerImport and use ONNX models
Microsoft.ML.TensorFlowImport and use TensorFlow models
Microsoft.ML.LightGbmLightGBM algorithms (gradient boosting)
Microsoft.Extensions.MLASP.NET Core integration (PredictionEnginePool)

Overall Project Architecture

graph TB
    subgraph "SmartBrew.WebApp — Blazor"
        LD["LanguageDetection.razor\nTextChanged() -> Predict()"]
        CD["BeverageDetection.razor\nLoadFileAsync() -> Predict()"]
    end

    subgraph "ML.NET Models — Generated by Model Builder"
        LDM["LanguageDetectionModel\n.mbconfig / .mlnet\n.consumption.cs / .training.cs"]
        CDM["BeverageDetectionModel\n.mbconfig / .mlnet\n.consumption.cs / .training.cs"]
    end

    subgraph "Training Data"
        CSV["languageDetection.csv\nLanguage, Message\n4->5 languages"]
        IMG["Images/\ncoffee/ goat/ other/\n28 images total"]
    end

    subgraph "ML Tools"
        MB["Model Builder\n(VS 2022 Extension)"]
        AUTO["AutoML\nMicrosoft.ML.AutoML"]
        MLNET["ML.NET\nMicrosoft.ML"]
    end

    subgraph "Infrastructure"
        CPU["Local CPU\n(Data classification)"]
        GPU["Local GPU NVIDIA CUDA\n(Image classification)"]
    end

    LD -->|"string Message -> PredictedLabel"| LDM
    CD -->|"byte[] ImageSource -> PredictedLabel"| CDM
    CSV -->|trains| LDM
    IMG -->|trains| CDM
    MB -->|"generates C# code + .mlnet"| LDM
    MB -->|"generates C# code + .mlnet"| CDM
    MB --> AUTO
    AUTO --> MLNET
    LDM -->|"SDCA / L-BFGS"| CPU
    CDM -->|"deep neural network"| GPU

End-to-End Flow — From Raw Data to Prediction

flowchart TD
    RAW["Raw data\n(CSV or images)"]
    PREP["Data preparation\n(cleaning, structuring)"]
    MB["Model Builder / AutoML\n(scenario, data, training)"]
    MLNET_MODEL[".mlnet\n(trained model)"]
    CS_CODE[".consumption.cs\n(generated C# code)"]
    APP[".NET Application\n(Blazor, ASP.NET, console...)"]
    PRED["Prediction\n(PredictedLabel + Score[])"]
    UI["User interface\n(display result)"]

    RAW --> PREP
    PREP --> MB
    MB --> MLNET_MODEL
    MB --> CS_CODE
    MLNET_MODEL --> CS_CODE
    CS_CODE --> APP
    APP -->|"ModelInput"| PRED
    PRED --> UI

Documentation enriched from the “Building Apps with Machine Learning in .NET” course and the official documentation at learn.microsoft.com/dotnet/machine-learning.


Search Terms

.net · apps · machine · ml · platforms · deployment · data · science · model · image · classification · ml.net · builder · transformations · generated · retraining · application · automl · available · configuration · detection · flow · scenario · workflow

Interested in this course?

Contact us to book it or get a custom training plan for your team.