8000 Add onnx export (regression) sample (#891) · dotnet/machinelearning-samples@0936bf4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0936bf4

Browse files
author
Bri Achtman
authored
Add onnx export (regression) sample (#891)
* add readme and project * update readme * add comments and update readme * Change name of folder * update readme + file structure * update readme for other onnx sample * update file paths * add section about exporting specific columns
1 parent 8167306 commit 0936bf4

File tree

6 files changed

+269
-1
lines changed

6 files changed

+269
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ The official ML.NET samples are divided in multiple categories depending on the
125125
<tr>
126126
<td align="middle"><img src="images/database.png" alt="Database chart"><br><img src="images/app-type-getting-started-term-cursor.png" alt="Getting started icon"><br><b>Loading data with LoadFromEnumerable<br><a href="samples/csharp/getting-started/DatabaseIntegration">C#</a><b></td>
127127
<td align="middle"><img src="images/model-explain-smaller.png" alt="Model explainability chart"><br><img src="images/app-type-e2e-black.png" alt="End-to-end app icon"><br><b>Model Explainability<br><a href="samples/csharp/end-to-end-apps/Model-Explainability">C#</a></b></td>
128+
<td align="middle"><img src="images/model-explain-smaller.png" alt="Model explainability chart"><br><img src="images/app-type-e2e-black.png" alt="End-to-end app icon"><br><b>Export to ONNX<br><a href="samples/csharp/getting-started/ONNXExport">C#</a></b></td>
128129
</tr>
129130
</table>
130131

samples/csharp/getting-started/DeepLearning_ObjectDetection_Onnx/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The dataset contains images which are located in the [assets](./ObjectDetectionC
1717
## Pre-trained model
1818
There are multiple models which are pre-trained for identifying multiple objects in the images. here we are using the pretrained model, **Tiny Yolo2** in **ONNX** format. This model is a real-time neural network for object detection that detects 20 different classes. It is made up of 9 convolutional layers and 6 max-pooling layers and is a smaller version of the more complex full [YOLOv2](https://pjreddie.com/darknet/yolov2/) network.
1919

20-
The Open Neural Network eXchange i.e [ONNX](http://onnx.ai/) is an open format to represent deep learning models. With ONNX, developers can move models between state-of-the-art tools and choose the combination that is best for them. ONNX is developed and supported by a community of partners.
20+
The Open Neural Network Exchange i.e [ONNX](http://onnx.ai/) is an open format to represent deep learning models. With ONNX, developers can move models between state-of-the-art tools and choose the combination that is best for them. ONNX is developed and supported by a community of partners.
2121

2222
The model is downloaded from the [ONNX Model Zoo](https://github.com/onnx/models) which is a is a collection of pre-trained, state-of-the-art models in the ONNX format.
2323

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.31025.218
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ONNXExport", "ONNXExport\ONNXExport.csproj", "{6F92E373-62D0-41E7-83C9-93DC2AEC4ADB}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{6F92E373-62D0-41E7-83C9-93DC2AEC4ADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{6F92E373-62D0-41E7-83C9-93DC2AEC4ADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{6F92E373-62D0-41E7-83C9-93DC2AEC4ADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{6F92E373-62D0-41E7-83C9-93DC2AEC4ADB}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {247BB18A-BBED-49BB-8A61-218E13BFF44F}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net5.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.ML" Version="1.5.5" />
10+
<PackageReference Include="Microsoft.ML.OnnxConverter" Version="0.17.5" />
11+
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.7.0" />
12+
<PackageReference Include="Microsoft.ML.OnnxTransformer" Version="1.5.5" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices.ComTypes;
6+
using Microsoft.ML;
7+
using Microsoft.ML.Data;
8+
9+
namespace ONNXExport
10+
{
11+
class Program
12+
{
13+
private static string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
14+
private static string TRAIN_DATA_FILEPATH = Path.Combine(currentDirectory, @"..\..\..\..\..\Regression_TaxiFarePrediction\TaxiFarePrediction\Data\taxi-fare-train.csv");
15+
private static string TEST_DATA_FILEPATH = Path.Combine(currentDirectory, @"..\..\..\..\..\Regression_TaxiFarePrediction\TaxiFarePrediction\Data\taxi-fare-test.csv");
16+
17+
static void Main(string[] args)
18+
{
19+
var mlContext = new MLContext();
20+
21+
// Load training data
22+
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<ModelInput>(
23+
path: TRAIN_DATA_FILEPATH,
24+
hasHeader: true,
25+
separatorChar: ',');
26+
27+
// Load test data
28+
IDataView testDataView = mlContext.Data.LoadFromTextFile<ModelInput>(
29+
path: TEST_DATA_FILEPATH,
30+
hasHeader: true,
31+
separatorChar: ',');
32+
33+
34+
// Create data processing pipeline for training ML.NET model
35+
var dataProcessPipeline = mlContext.Transforms.Categorical.OneHotEncoding(new[] { new InputOutputColumnPair("vendor_id", "vendor_id"), new InputOutputColumnPair("payment_type", "payment_type") })
36+
.Append(mlContext.Transforms.Concatenate("Features", new[] { "vendor_id", "payment_type", "rate_code", "passenger_count", "trip_time_in_secs", "trip_distance" }));
37+
38+
// Set training algorithm and append to pipeline
39+
var trainer = mlContext.Regression.Trainers.Sdca(labelColumnName: "fare_amount", featureColumnName: "Features");
40+
41+
var trainingPipeline = dataProcessPipeline.Append(trainer);
42+
43+
// Train ML.NET model on training data
44+
ITransformer model = trainingPipeline.Fit(trainingDataView);
45+
46+
// You need a transformer and input data to convert an ML.NET model to an ONNX model
47+
// By default, the ONNX conversion will generate the ONNX file with the latest OpSet version
48+
using (var stream = File.Create("taxi-fare-model.onnx"))
49+
mlContext.Model.ConvertToOnnx(model, trainingDataView, stream);
50+
51+
// Now you can compare the results from the ML.NET and ONNX models
52+
53+
// Create the pipeline using the ONNX file
54+
var onnxModelPath = "taxi-fare-model.onnx";
55+
var onnxEstimator = mlContext.Transforms.ApplyOnnxModel(onnxModelPath);
56+
57+
// Make sure to either use the 'using' clause or explicitly dispose the returned onnxTransformer to prevent memory leaks
58+
using var onnxTransformer = onnxEstimator.Fit(trainingDataView);
59+
60+
// Inference on the test set with the ML.NET model
61+
var output = model.Transform(testDataView);
62+
63+
// Inference on the test set with the ONNX model
64+
var onnxOutput = onnxTransformer.Transform(testDataView);
65+
66+
//Get the Scores from ML.NET model
67+
var outScores = mlContext.Data.CreateEnumerable<ScoreValue>(output, reuseRowObject: false);
68+
69+
//Get the Scores from ONNX model
70+
var onnxOutScores = mlContext.Data.CreateEnumerable<OnnxScoreValue>(onnxOutput, reuseRowObject: false);
71+
72+
// Print Scores from ML.NET model
73+
PrintScore(outScores, 5);
74+
75+
// Print Scores from ONNX model
76+
PrintScore(onnxOutScores, 5);
77+
}
78+
79+
// Define model input schema
80+
public class ModelInput
81+
{
82+
[ColumnName("vendor_id"), LoadColumn(0)]
83+
public string Vendor_id { get; set; }
84+
85+
86+
[ColumnName("rate_code"), LoadColumn(1)]
87+
public float Rate_code { get; set; }
88+
89+
90+
[ColumnName("passenger_count"), LoadColumn(2)]
91+
public float Passenger_count { get; set; }
92+
93+
94+
[ColumnName("trip_time_in_secs"), LoadColumn(3)]
95+
public float Trip_time_in_secs { get; set; }
96+
97+
98+
[ColumnName("trip_distance"), LoadColumn(4)]
99+
public float Trip_distance { get; set; }
100+
101+
102+
[ColumnName("payment_type"), LoadColumn(5)]
103+
public string Payment_type { get; set; }
104+
105+
106+
[ColumnName("fare_amount"), LoadColumn(6)]
107+
public float Fare_amount { get; set; }
108+
109+
}
110+
111+
// Define model output schema
112+
public class ModelOutput
113+
{
114+
public float Score { get; set; }
115+
}
116+
117+
private class ScoreValue
118+
{
119+
public float Score { get; set; }
120+
}
121+
122+
private class OnnxScoreValue
123+
{
124+
public VBuffer<float> Score { get; set; }
125+
}
126+
127+
private static void PrintScore(IEnumerable<ScoreValue> values, int numRows)
128+
{
129+
Console.WriteLine("Predicted Scores with ML.NET model");
130+
foreach (var value in values.Take(numRows))
131+
Console.WriteLine("{0, -10} {1, -10}", "Score", value.Score);
132+
}
133+
134+
private static void PrintScore(IEnumerable<OnnxScoreValue> values, int numRows)
135+
{
136+
Console.WriteLine("Predicted Scores with ONNX model");
137+
foreach (var value in values.Take(numRows))
138+
Console.WriteLine("{0, -10} {1, -10}", "Score", value.Score.GetItemOrDefault(0));
139+
}
140+
}
141+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Exporting an ML.NET model to ONNX
2+
3+
| ML.NET version | API type | Status | App Type | Data type | Scenario | ML Task | Algorithms |
4+
|----------------|-------------------|-------------------------------|-------------|-----------|---------------------|---------------------------|-----------------------------|
5+
| v1.5.5 | Dynamic API | Up-to-date | Console app | .csv files | Price prediction | Regression | Light GBM regression |
6+
7+
In this sample, you'll see how to use ML.NET to train a regression model and then convert that model to the ONNX format.
8+
9+
## Problem
10+
11+
The Open Neural Network Exchange i.e [ONNX](http://onnx.ai/) is an open format to represent deep learning models. With ONNX, developers can move models between state-of-the-art tools and choose the combination that is best for them. ONNX is developed and supported by a community of partners.
12+
13+
There may be times when you want to train a model with ML.NET and then convert to ONNX, for instance if you want to consume your model with WinML to take advantage of GPU inferencing in Windows applications.
14+
15+
Not all ML.NET models can be converted to ONNX; it is dependent on the trainers and transforms in the training pipeline. For a list of supported trainers see the tables in the ML.NET [Algorithms Doc](https://docs.microsoft.com/dotnet/machine-learning/how-to-choose-an-ml-net-algorithm) and for a list of supported transforms check out the [Data transforms Doc](https://docs.microsoft.com/dotnet/machine-learning/resources/transforms).
16+
17+
## Dataset
18+
19+
This sample uses the [NYC Taxi Fare dataset](https://github.com/dotnet/machinelearning-samples/blob/main/datasets/README.md#nyc-taxi-fare) for training.
20+
21+
## Solution
22+
23+
The console application project `ONNXExport` can be used to train an ML.NET model that predicts the price of taxi fare based on several features such as distance travelled and number of passengers, to export that model to ONNX, and then to consume the ONNX model and make predictions with it.
24+
25+
### NuGet Packages
26+
27+
To export an ML.NET model to ONNX, you must install the following NuGet packages in your project:
28+
29+
- Microsoft.ML.OnnxConverter
30+
31+
You must also install:
32+
33+
- Microsoft.ML for training the ML.NET model
34+
- Microsoft.ML.ONNXRuntime and Microsoft.ML.OnnxTransformer for scoring the ONNX model
35+
36+
### Transforms and trainers
37+
38+
This pipeline contains the following transforms and trainers which are all ONNX exportable:
39+
40+
- OneHotEncoding transform
41+
- Concatenate transform
42+
- Light GBM trainer
43+
44+
### Code
45+
46+
After training an ML.NET model, you can use the following code to convert to ONNX:
47+
48+
```csharp
49+
using (var stream = File.Create("taxi-fare-model.onnx"))
50+
mlContext.Model.ConvertToOnnx(model, trainingDataView, stream);
51+
```
52+
53+
You need a transformer and input data to convert an ML.NET model to an ONNX model. By default, the ONNX conversion will generate the ONNX file with the latest OpSet version
54+
55+
After converting to ONNX, you can then consume the ONNX model with the following code:
56+
57+
```csharp
58+
var onnxEstimator = mlContext.Transforms.ApplyOnnxModel(onnxModelPath);
59+
60+
using var onnxTransformer = onnxEstimator.Fit(trainingDataView);
61+
62+
var onnxOutput = onnxTransformer.Transform(testDataView);
63+
```
64+
65+
You should get the same results when comparing the ML.NET model and ONNX model on the same sample input. If you run the project, you should get similar to the following output in the console:
66+
67+
```console
68+
Predicted Scores with ML.NET model
69+
Score 19.60645
70+
Score 18.673796
71+
Score 5.9175444
72+
Score 4.8969507
7FD8 73+
Score 19.108932
74+
Predicted Scores with ONNX model
75+
Score 19.60645
76+
Score 18.673796
77+
Score 5.9175444
78+
Score 4.8969507
79+
Score 19.108932
80+
```
81+
82+
## Performance
83+
84+
The default ONNX to ML.NET conversion is not optimal and produces extra graph outputs that are not needed for ML.NET usage. ONNX Runtime does reverse depth first search which results in a lot of conversion operations of native memory to managed memory from ONNX Runtime to ML.NET and execution of more than the necessary kernels.
85+
86+
If you specify just the necessary graph outputs, it will only execute a subset of the graph. Thus, by eliminating all graph outputs except Score, you can improve inference performance.

0 commit comments

Comments
 (0)
0