Skip to content

Commit

Permalink
Expose the Onnx runtime option for setting the number of threads (#5962)
Browse files Browse the repository at this point in the history
  • Loading branch information
yaeldMS committed Oct 12, 2021
1 parent 1dfccca commit 15eeef7
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 28 deletions.
15 changes: 13 additions & 2 deletions src/Microsoft.ML.OnnxTransformer/OnnxCatalog.cs
Expand Up @@ -96,8 +96,19 @@ public static class OnnxCatalog
string modelFile,
int? gpuDeviceId = null,
bool fallbackToCpu = false)
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), new[] { outputColumnName }, new[] { inputColumnName },
modelFile, gpuDeviceId, fallbackToCpu);
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), new[] { outputColumnName }, new[] { inputColumnName },
modelFile, gpuDeviceId, fallbackToCpu);

/// <summary>
/// Create a <see cref="OnnxScoringEstimator"/> using the specified <see cref="OnnxOptions"/>.
/// Please refer to <see cref="OnnxScoringEstimator"/> to learn more about the necessary dependencies,
/// and how to run it on a GPU.
/// </summary>
/// <param name="catalog">The transform's catalog.</param>
/// <param name="options">Options for the <see cref="OnnxScoringEstimator"/>.</param>
public static OnnxScoringEstimator ApplyOnnxModel(this TransformsCatalog catalog, OnnxOptions options)
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), options.OutputColumns, options.InputColumns, options.ModelFile,
options.GpuDeviceId, options.FallbackToCpu, options.ShapeDictionary, options.RecursionLimit, options.InterOpNumThreads, options.IntraOpNumThreads);

/// <summary>
/// Create a <see cref="OnnxScoringEstimator"/>, which applies a pre-trained Onnx model to the <paramref name="inputColumnName"/> column.
Expand Down
59 changes: 59 additions & 0 deletions src/Microsoft.ML.OnnxTransformer/OnnxOptions.cs
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

namespace Microsoft.ML.Transforms.Onnx
{
/// <summary>
/// The options for an <see cref="OnnxScoringEstimator"/>.
/// </summary>
public sealed class OnnxOptions
{
/// <summary>
/// Path to the onnx model file.
/// </summary>
public string ModelFile;

/// <summary>
/// Name of the input column.
/// </summary>
public string[] InputColumns;

/// <summary>
/// Name of the output column.
/// </summary>
public string[] OutputColumns;

/// <summary>
/// GPU device id to run on (e.g. 0,1,..). Null for CPU. Requires CUDA 9.1.
/// </summary>
public int? GpuDeviceId = null;

/// <summary>
/// If true, resumes execution on CPU upon GPU error. If false, will raise the GPU exception.
/// </summary>
public bool FallbackToCpu = false;

/// <summary>
/// ONNX shapes to be used over those loaded from <see cref="ModelFile"/>.
/// </summary>
public IDictionary<string, int[]> ShapeDictionary;

/// <summary>
/// Protobuf CodedInputStream recursion limit.
/// </summary>
public int RecursionLimit = 100;

/// <summary>
/// Controls the number of threads used to parallelize the execution of the graph (across nodes).
/// </summary>
public int? InterOpNumThreads = null;

/// <summary>
/// Controls the number of threads to use to run the model.
/// </summary>
public int? IntraOpNumThreads = null;
}
}
25 changes: 20 additions & 5 deletions src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs
Expand Up @@ -90,6 +90,12 @@ internal sealed class Options : TransformInputBase

[Argument(ArgumentType.AtMostOnce, HelpText = "Protobuf CodedInputStream recursion limit.", SortOrder = 6)]
public int RecursionLimit = 100;

[Argument(ArgumentType.AtMostOnce, HelpText = "Controls the number of threads used to parallelize the execution of the graph (across nodes).", SortOrder = 7)]
public int? InterOpNumThreads = null;

[Argument(ArgumentType.AtMostOnce, HelpText = "Controls the number of threads to use to run the model.", SortOrder = 8)]
public int? IntraOpNumThreads = null;
}

/// <summary>
Expand Down Expand Up @@ -244,7 +250,8 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Dat
Host.CheckNonWhiteSpace(options.ModelFile, nameof(options.ModelFile));
Host.CheckIO(File.Exists(options.ModelFile), "Model file {0} does not exists.", options.ModelFile);
// Because we cannot delete the user file, ownModelFile should be false.
Model = new OnnxModel(options.ModelFile, options.GpuDeviceId, options.FallbackToCpu, ownModelFile: false, shapeDictionary: shapeDictionary, options.RecursionLimit);
Model = new OnnxModel(options.ModelFile, options.GpuDeviceId, options.FallbackToCpu, ownModelFile: false, shapeDictionary: shapeDictionary, options.RecursionLimit,
options.InterOpNumThreads, options.IntraOpNumThreads);
}
else
{
Expand Down Expand Up @@ -309,8 +316,11 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Dat
/// <param name="fallbackToCpu">If GPU error, raise exception or fallback to CPU.</param>
/// <param name="shapeDictionary"></param>
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
internal OnnxTransformer(IHostEnvironment env, string[] outputColumnNames, string[] inputColumnNames, string modelFile, int? gpuDeviceId = null, bool fallbackToCpu = false,
IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
int? interOpNumThreads = null, int? intraOpNumThreads = null)
: this(env, new Options()
{
ModelFile = modelFile,
Expand All @@ -319,7 +329,9 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Dat
GpuDeviceId = gpuDeviceId,
FallbackToCpu = fallbackToCpu,
CustomShapeInfos = shapeDictionary?.Select(pair => new CustomShapeInfo(pair.Key, pair.Value)).ToArray(),
RecursionLimit = recursionLimit
RecursionLimit = recursionLimit,
InterOpNumThreads = interOpNumThreads,
IntraOpNumThreads = intraOpNumThreads
})
{
}
Expand Down Expand Up @@ -856,9 +868,12 @@ public sealed class OnnxScoringEstimator : TrivialEstimator<OnnxTransformer>
/// <param name="fallbackToCpu">If GPU error, raise exception or fallback to CPU.</param>
/// <param name="shapeDictionary"></param>
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
internal OnnxScoringEstimator(IHostEnvironment env, string[] outputColumnNames, string[] inputColumnNames, string modelFile,
int? gpuDeviceId = null, bool fallbackToCpu = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
: this(env, new OnnxTransformer(env, outputColumnNames, inputColumnNames, modelFile, gpuDeviceId, fallbackToCpu, shapeDictionary, recursionLimit))
int? gpuDeviceId = null, bool fallbackToCpu = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
int? interOpNumThreads = null, int? intraOpNumThreads = null)
: this(env, new OnnxTransformer(env, outputColumnNames, inputColumnNames, modelFile, gpuDeviceId, fallbackToCpu, shapeDictionary, recursionLimit, interOpNumThreads, intraOpNumThreads))
{
}

Expand Down
21 changes: 18 additions & 3 deletions src/Microsoft.ML.OnnxTransformer/OnnxUtils.cs
Expand Up @@ -165,8 +165,11 @@ public OnnxVariableInfo(string name, OnnxShape shape, Type typeInOnnxRuntime, Da
/// no longer needed.</param>
/// <param name="shapeDictionary"></param>
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
public OnnxModel(string modelFile, int? gpuDeviceId = null, bool fallbackToCpu = false,
bool ownModelFile = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
bool ownModelFile = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
int? interOpNumThreads = null, int? intraOpNumThreads = null)
{
// If we don't own the model file, _disposed should be false to prevent deleting user's file.
_disposed = false;
Expand All @@ -181,15 +184,27 @@ public OnnxVariableInfo(string name, OnnxShape shape, Type typeInOnnxRuntime, Da
catch (OnnxRuntimeException)
{
if (fallbackToCpu)
_session = new InferenceSession(modelFile);
{
var sessionOptions = new SessionOptions()
{
InterOpNumThreads = interOpNumThreads.GetValueOrDefault(),
IntraOpNumThreads = intraOpNumThreads.GetValueOrDefault()
};
_session = new InferenceSession(modelFile, sessionOptions);
}
else
// If called from OnnxTransform, is caught and rethrown
throw;
}
}
else
{
_session = new InferenceSession(modelFile);
var sessionOptions = new SessionOptions()
{
InterOpNumThreads = interOpNumThreads.GetValueOrDefault(),
IntraOpNumThreads = intraOpNumThreads.GetValueOrDefault()
};
_session = new InferenceSession(modelFile, sessionOptions);
}

try
Expand Down
50 changes: 32 additions & 18 deletions test/Microsoft.ML.OnnxTransformerTest/OnnxTransformTests.cs
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

Expand Down Expand Up @@ -119,8 +119,10 @@ public OnnxTransformTests(ITestOutputHelper output) : base(output)
{
}

[OnnxFact]
public void TestSimpleCase()
[OnnxTheory]
[InlineData(false)]
[InlineData(true)]
public void TestSimpleCase(bool useOptionsCtor)
{
var modelFile = "squeezenet/00000001/model.onnx";
var samplevector = GetSampleArrayData();
Expand All @@ -139,7 +141,19 @@ public void TestSimpleCase()
var xyData = new List<TestDataXY> { new TestDataXY() { A = new float[InputSize] } };
var stringData = new List<TestDataDifferntType> { new TestDataDifferntType() { data_0 = new string[InputSize] } };
var sizeData = new List<TestDataSize> { new TestDataSize() { data_0 = new float[2] } };
var pipe = ML.Transforms.ApplyOnnxModel(new[] { "softmaxout_1" }, new[] { "data_0" }, modelFile, gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu);
var options = new OnnxOptions()
{
OutputColumns = new[] { "softmaxout_1" },
InputColumns = new[] {"data_0" },
ModelFile = modelFile,
GpuDeviceId = _gpuDeviceId,
FallbackToCpu = _fallbackToCpu,
InterOpNumThreads = 1,
IntraOpNumThreads = 1
};
var pipe = useOptionsCtor ?
ML.Transforms.ApplyOnnxModel(options) :
ML.Transforms.ApplyOnnxModel(options.OutputColumns, options.InputColumns, modelFile, gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu);

var invalidDataWrongNames = ML.Data.LoadFromEnumerable(xyData);
var invalidDataWrongTypes = ML.Data.LoadFromEnumerable(stringData);
Expand Down Expand Up @@ -713,14 +727,14 @@ public void TestOnnxModelNotDisposal()

private class OnnxMapInput
{
[OnnxMapType(typeof(int),typeof(float))]
public IDictionary<int,float> Input { get; set; }
[OnnxMapType(typeof(int), typeof(float))]
public IDictionary<int, float> Input { get; set; }
}

private class OnnxMapOutput
{
[OnnxMapType(typeof(int),typeof(float))]
public IDictionary<int,float> Output { get; set; }
[OnnxMapType(typeof(int), typeof(float))]
public IDictionary<int, float> Output { get; set; }
}

/// <summary>
Expand Down Expand Up @@ -753,10 +767,10 @@ public void SmokeInMemoryOnnxMapTypeTest()
var transformedDataView = model.Transform(dataView);
var transformedDataPoints = ML.Data.CreateEnumerable<OnnxMapOutput>(transformedDataView, false).ToList();

for(int i = 0; i < dataPoints.Count(); ++i)
for (int i = 0; i < dataPoints.Count(); ++i)
{
Assert.Equal(dataPoints[i].Input.Count(), transformedDataPoints[i].Output.Count());
foreach(var pair in dataPoints[i].Input)
foreach (var pair in dataPoints[i].Input)
Assert.Equal(pair.Value, transformedDataPoints[i].Output[pair.Key + 1]);
}
}
Expand Down Expand Up @@ -815,7 +829,7 @@ public void TestOnnxTransformWithCustomShapes()
transformedDataViews[2] = onnxTransformer[2].Transform(dataView);

// Conduct the same check for all the 3 called public APIs.
foreach(var transformedDataView in transformedDataViews)
foreach (var transformedDataView in transformedDataViews)
{
var transformedDataPoints = ML.Data.CreateEnumerable<PredictionWithCustomShape>(transformedDataView, false).ToList();

Expand Down Expand Up @@ -901,32 +915,32 @@ public void SpecifyOnnxShapes()
Assert.False(somethingWrong);

// Case 3: this shape conflicts with output shape [1, 1, 1, 5] loaded from the model.
shapeDictionary= new Dictionary<string, int[]>() {
shapeDictionary = new Dictionary<string, int[]>() {
{ "outb", new int[] { 5, 6 } },
};
somethingWrong= false;
somethingWrong = false;
try
{
TryModelWithCustomShapesHelper(shapeDictionary);
}
catch
{
somethingWrong= true;
somethingWrong = true;
}
Assert.True(somethingWrong);

// Case 4: this shape works with output shape [1, 1, 1, 5] loaded from the model.
shapeDictionary= new Dictionary<string, int[]>() {
shapeDictionary = new Dictionary<string, int[]>() {
{ "outb", new int[] { -1, -1, -1, -1 } },
};
somethingWrong= false;
somethingWrong = false;
try
{
TryModelWithCustomShapesHelper(shapeDictionary);
}
catch
{
somethingWrong= true;
somethingWrong = true;
}
Assert.False(somethingWrong);
}
Expand Down Expand Up @@ -1024,7 +1038,7 @@ public void TestOnnxTransformSaveAndLoadWithRecursionLimit()
var pipe = ML.Transforms.LoadImages("data_0", imageFolder, "imagePath")
.Append(ML.Transforms.ResizeImages("data_0", imageHeight, imageWidth))
.Append(ML.Transforms.ExtractPixels("data_0", interleavePixelColors: true))
.Append(ML.Transforms.ApplyOnnxModel(new []{ "softmaxout_1" }, new []{ "data_0" }, modelFile,
.Append(ML.Transforms.ApplyOnnxModel(new[] { "softmaxout_1" }, new[] { "data_0" }, modelFile,
gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu, shapeDictionary: null, recursionLimit: 50));

TestEstimatorCore(pipe, data);
Expand Down

0 comments on commit 15eeef7

Please sign in to comment.