From ad90352a6888320c539d8e4e8844e63ddca265ab Mon Sep 17 00:00:00 2001 From: Scott McKay Date: Tue, 18 Jul 2023 08:21:18 +1000 Subject: [PATCH] Add MAUI test app that can be used to test model loading and performance (#16658) ### Description MAUI test app with tooling to add model and generated or provided input test data. The app will load the model and validate the output. It can also run a specified number of iterations to provide basic performance information. image ### Motivation and Context Primarily to make it easier to test an arbitrary model on iOS. A MAUI app allows testing on all platforms. --------- Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com> --- csharp/.clang-format | 17 +- .../EqualityComparers.cs | 80 + .../InferenceTest.cs | 2 +- .../TestDataLoader.cs | 376 +- ...oft.ML.OnnxRuntime.Tests.NetCoreApp.csproj | 26 +- csharp/tools/MauiModelTester/App.xaml | 14 + csharp/tools/MauiModelTester/App.xaml.cs | 11 + csharp/tools/MauiModelTester/AppShell.xaml | 14 + csharp/tools/MauiModelTester/AppShell.xaml.cs | 9 + csharp/tools/MauiModelTester/MainPage.xaml | 65 + csharp/tools/MauiModelTester/MainPage.xaml.cs | 174 + .../MauiModelTester/MauiModelTester.csproj | 69 + .../tools/MauiModelTester/MauiModelTester.sln | 27 + csharp/tools/MauiModelTester/MauiProgram.cs | 24 + csharp/tools/MauiModelTester/NuGet.config | 14 + .../MauiModelTester/OrtInferenceSession.cs | 115 + csharp/tools/MauiModelTester/PerfStats.cs | 71 + .../Platforms/Android/AndroidManifest.xml | 6 + .../Platforms/Android/MainActivity.cs | 10 + .../Platforms/Android/MainApplication.cs | 15 + .../Android/Resources/values/colors.xml | 6 + .../Platforms/Windows/App.xaml | 8 + .../Platforms/Windows/App.xaml.cs | 24 + .../Platforms/Windows/Package.appxmanifest | 46 + .../Platforms/iOS/AppDelegate.cs | 9 + .../MauiModelTester/Platforms/iOS/Info.plist | 32 + .../MauiModelTester/Platforms/iOS/Program.cs | 15 + .../Properties/launchSettings.json | 8 + csharp/tools/MauiModelTester/ReadMe.md | 80 + .../Resources/AppIcon/onnxruntime_icon.png | Bin 0 -> 8389 bytes .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107184 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111076 bytes .../Resources/Raw/AboutAssets.txt | 15 + .../Resources/Raw/test_data/model.onnx | 59296 ++++++++++++++++ .../Resources/Raw/test_data/model_info.txt | 1 + .../Raw/test_data/test_data_set_0/input_0.pb | Bin 0 -> 714455 bytes .../Raw/test_data/test_data_set_0/output_0.pb | Bin 0 -> 4018 bytes .../Resources/Splash/onnxruntime_logo.png | Bin 0 -> 23597 bytes .../Resources/Styles/Colors.xaml | 44 + .../Resources/Styles/Styles.xaml | 405 + csharp/tools/MauiModelTester/Utils.cs | 247 + .../tools/MauiModelTester/create_test_data.py | 159 + tools/python/onnx_test_data_utils.py | 12 +- tools/python/ort_test_dir_utils.py | 32 +- 44 files changed, 61316 insertions(+), 262 deletions(-) create mode 100644 csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/EqualityComparers.cs create mode 100644 csharp/tools/MauiModelTester/App.xaml create mode 100644 csharp/tools/MauiModelTester/App.xaml.cs create mode 100644 csharp/tools/MauiModelTester/AppShell.xaml create mode 100644 csharp/tools/MauiModelTester/AppShell.xaml.cs create mode 100644 csharp/tools/MauiModelTester/MainPage.xaml create mode 100644 csharp/tools/MauiModelTester/MainPage.xaml.cs create mode 100644 csharp/tools/MauiModelTester/MauiModelTester.csproj create mode 100644 csharp/tools/MauiModelTester/MauiModelTester.sln create mode 100644 csharp/tools/MauiModelTester/MauiProgram.cs create mode 100644 csharp/tools/MauiModelTester/NuGet.config create mode 100644 csharp/tools/MauiModelTester/OrtInferenceSession.cs create mode 100644 csharp/tools/MauiModelTester/PerfStats.cs create mode 100644 csharp/tools/MauiModelTester/Platforms/Android/AndroidManifest.xml create mode 100644 csharp/tools/MauiModelTester/Platforms/Android/MainActivity.cs create mode 100644 csharp/tools/MauiModelTester/Platforms/Android/MainApplication.cs create mode 100644 csharp/tools/MauiModelTester/Platforms/Android/Resources/values/colors.xml create mode 100644 csharp/tools/MauiModelTester/Platforms/Windows/App.xaml create mode 100644 csharp/tools/MauiModelTester/Platforms/Windows/App.xaml.cs create mode 100644 csharp/tools/MauiModelTester/Platforms/Windows/Package.appxmanifest create mode 100644 csharp/tools/MauiModelTester/Platforms/iOS/AppDelegate.cs create mode 100644 csharp/tools/MauiModelTester/Platforms/iOS/Info.plist create mode 100644 csharp/tools/MauiModelTester/Platforms/iOS/Program.cs create mode 100644 csharp/tools/MauiModelTester/Properties/launchSettings.json create mode 100644 csharp/tools/MauiModelTester/ReadMe.md create mode 100644 csharp/tools/MauiModelTester/Resources/AppIcon/onnxruntime_icon.png create mode 100644 csharp/tools/MauiModelTester/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 csharp/tools/MauiModelTester/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 csharp/tools/MauiModelTester/Resources/Raw/AboutAssets.txt create mode 100644 csharp/tools/MauiModelTester/Resources/Raw/test_data/model.onnx create mode 100644 csharp/tools/MauiModelTester/Resources/Raw/test_data/model_info.txt create mode 100644 csharp/tools/MauiModelTester/Resources/Raw/test_data/test_data_set_0/input_0.pb create mode 100644 csharp/tools/MauiModelTester/Resources/Raw/test_data/test_data_set_0/output_0.pb create mode 100644 csharp/tools/MauiModelTester/Resources/Splash/onnxruntime_logo.png create mode 100644 csharp/tools/MauiModelTester/Resources/Styles/Colors.xaml create mode 100644 csharp/tools/MauiModelTester/Resources/Styles/Styles.xaml create mode 100644 csharp/tools/MauiModelTester/Utils.cs create mode 100644 csharp/tools/MauiModelTester/create_test_data.py diff --git a/csharp/.clang-format b/csharp/.clang-format index f2748cf3ab..f440737ccf 100644 --- a/csharp/.clang-format +++ b/csharp/.clang-format @@ -1,15 +1,22 @@ --- # clang-format settings for the C# code -BasedOnStyle: Microsoft +BasedOnStyle: Microsoft + +# Setting ColumnLimit to 0 so developer choices about where to break lines are maintained. +# Developers are responsible for adhering to the 120 character maximum. +ColumnLimit: 0 BreakBeforeBraces: Custom BraceWrapping: - AfterCaseLabel: true - BeforeWhile: true + AfterCaseLabel: true + BeforeWhile: true SplitEmptyFunction: false SplitEmptyRecord: false - # unfortunately there's no config option for handling the 'get' or 'set' of properties + # unfortunately there's no config option for handling the 'get' or 'set' of properties IndentCaseLabels: true KeepEmptyLinesAtTheStartOfBlocks: false -SpacesInContainerLiterals: false \ No newline at end of file +NamespaceIndentation: All +SpacesInContainerLiterals: false +SortIncludes: CaseSensitive +SortUsingDeclarations: LexicographicNumeric diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/EqualityComparers.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/EqualityComparers.cs new file mode 100644 index 0000000000..44a9f8acf0 --- /dev/null +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/EqualityComparers.cs @@ -0,0 +1,80 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System; +using System.Collections.Generic; + +namespace Microsoft.ML.OnnxRuntime.Tests +{ + internal class FloatComparer : IEqualityComparer + { + private float atol = 1e-3f; + private float rtol = 1.7e-2f; + + public bool Equals(float x, float y) + { + return Math.Abs(x - y) <= (atol + rtol * Math.Abs(y)); + } + public int GetHashCode(float x) + { + return x.GetHashCode(); + } + } + + internal class DoubleComparer : IEqualityComparer + { + private double atol = 1e-3; + private double rtol = 1.7e-2; + + public bool Equals(double x, double y) + { + return Math.Abs(x - y) <= (atol + rtol * Math.Abs(y)); + } + public int GetHashCode(double x) + { + return x.GetHashCode(); + } + } + + internal class ExactComparer : IEqualityComparer + { + public bool Equals(T x, T y) + { + return x.Equals(y); + } + public int GetHashCode(T x) + { + return x.GetHashCode(); + } + } + + /// + /// Use it to compare Float16 + /// + internal class Float16Comparer : IEqualityComparer + { + public ushort tolerance = 0; + public bool Equals(Float16 x, Float16 y) + { + return Math.Abs(x.value - y.value) <= (tolerance + y.value); + } + public int GetHashCode(Float16 x) + { + return x.GetHashCode(); + } + } + + /// + /// Use it to compare Bloat16 + /// + internal class BFloat16Comparer : IEqualityComparer + { + public ushort tolerance = 0; + public bool Equals(BFloat16 x, BFloat16 y) + { + return Math.Abs(x.value - y.value) <= (tolerance + y.value); + } + public int GetHashCode(BFloat16 x) + { + return x.GetHashCode(); + } + } +} diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs index 78b992ad1f..7dde5b004e 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs @@ -1993,7 +1993,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests #endif var session = (deviceId.HasValue) ? new InferenceSession(model, option) - : new InferenceSession(model); + : new InferenceSession(model); float[] inputData = TestDataLoader.LoadTensorFromEmbeddedResource("bench.in"); float[] expectedOutput = TestDataLoader.LoadTensorFromEmbeddedResource("bench.expected_out"); var inputMeta = session.InputMetadata; diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs index 5c23663a6e..548f7cf238 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs @@ -15,10 +15,13 @@ namespace Microsoft.ML.OnnxRuntime.Tests public class DisposableListTest : List, IDisposableReadOnlyCollection where T : IDisposable { - public DisposableListTest() { } - public DisposableListTest(int count) : base(count) { } + public DisposableListTest() + {} + public DisposableListTest(int count) + : base(count) + {} - #region IDisposable Support +#region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) @@ -51,7 +54,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests Dispose(true); GC.SuppressFinalize(this); } - #endregion +#endregion } internal struct DisposableTestPair : IDisposable @@ -90,7 +93,6 @@ namespace Microsoft.ML.OnnxRuntime.Tests return model; } - internal static float[] LoadTensorFromEmbeddedResource(string path) { var tensorData = new List(); @@ -99,7 +101,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests var resourceName = assembly.GetManifestResourceNames().Single(p => p.EndsWith("." + path)); using (StreamReader inputFile = new StreamReader(assembly.GetManifestResourceStream(resourceName))) { - inputFile.ReadLine(); //skip the input name + inputFile.ReadLine(); // skip the input name string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < dataStr.Length; i++) { @@ -115,14 +117,14 @@ namespace Microsoft.ML.OnnxRuntime.Tests if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR) { throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" + - $" but loading as tensor: '{tensor.Name}'"); + $" but loading as tensor: '{tensor.Name}'"); } var protoDt = (Tensors.TensorElementType)tensor.DataType; var metaElementType = nodeMeta.ElementDataType; if (!((protoDt == metaElementType) || - (protoDt == TensorElementType.UInt16 && - (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16)))) + (protoDt == TensorElementType.UInt16 && + (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16)))) throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'"); // Tensors within Sequences may have no dimensions as the standard allows @@ -130,7 +132,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count) { throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " + - $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}"); + $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}"); } var intDims = new int[tensor.Dims.Count]; @@ -143,7 +145,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests { if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != tensor.Dims[i])) throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " + - $"is expected to either be -1 or {tensor.Dims[i]}"); + $"is expected to either be -1 or {tensor.Dims[i]}"); } // element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so @@ -155,7 +157,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests } internal static NamedOnnxValue CreateNamedOnnxValueFromTensorRawData(string nodeName, ReadOnlySpan rawData, - TensorElementType elementType, int[] intDims) + TensorElementType elementType, int[] intDims) { switch (elementType) { @@ -209,33 +211,33 @@ namespace Microsoft.ML.OnnxRuntime.Tests internal static NamedOnnxValue LoadOnnxValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta) { // No sparse tensor support yet - //Set buffer size to 4MB + // Set buffer size to 4MB const int readBufferSize = 4194304; using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize)) { switch (nodeMeta.OnnxValueType) { case OnnxValueType.ONNX_TYPE_TENSOR: - { - var tensor = Onnx.TensorProto.Parser.ParseFrom(file); - return LoadTensorPb(tensor, nodeName, nodeMeta); - } + { + var tensor = Onnx.TensorProto.Parser.ParseFrom(file); + return LoadTensorPb(tensor, nodeName, nodeMeta); + } case OnnxValueType.ONNX_TYPE_SEQUENCE: - { - var sequence = Onnx.SequenceProto.Parser.ParseFrom(file); - return CreateNamedOnnxValueFromSequence(sequence, nodeName, nodeMeta); - } + { + var sequence = Onnx.SequenceProto.Parser.ParseFrom(file); + return CreateNamedOnnxValueFromSequence(sequence, nodeName, nodeMeta); + } case OnnxValueType.ONNX_TYPE_MAP: - { - throw new NotImplementedException( - "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072"); - } + { + throw new NotImplementedException( + "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072"); + } case OnnxValueType.ONNX_TYPE_OPTIONAL: - { - var opt = Onnx.OptionalProto.Parser.ParseFrom(file); - return CreateNamedOnnxValueFromOptional(opt, nodeName, nodeMeta); - } + { + var opt = Onnx.OptionalProto.Parser.ParseFrom(file); + return CreateNamedOnnxValueFromOptional(opt, nodeName, nodeMeta); + } default: throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented"); } @@ -245,33 +247,33 @@ namespace Microsoft.ML.OnnxRuntime.Tests internal static DisposableTestPair LoadOrtValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta) { // No sparse tensor support yet - //Set buffer size to 4MB + // Set buffer size to 4MB const int readBufferSize = 4194304; using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize)) { switch (nodeMeta.OnnxValueType) { case OnnxValueType.ONNX_TYPE_TENSOR: - { - var tensor = Onnx.TensorProto.Parser.ParseFrom(file); - return new DisposableTestPair(nodeName, LoadOrValueTensorPb(tensor, nodeName, nodeMeta)); - } + { + var tensor = Onnx.TensorProto.Parser.ParseFrom(file); + return new DisposableTestPair(nodeName, LoadOrValueTensorPb(tensor, nodeName, nodeMeta)); + } case OnnxValueType.ONNX_TYPE_SEQUENCE: - { - var sequence = Onnx.SequenceProto.Parser.ParseFrom(file); - return new DisposableTestPair(nodeName, CreateOrtValueFromSequence(sequence, nodeName, nodeMeta)); - } + { + var sequence = Onnx.SequenceProto.Parser.ParseFrom(file); + return new DisposableTestPair(nodeName, CreateOrtValueFromSequence(sequence, nodeName, nodeMeta)); + } case OnnxValueType.ONNX_TYPE_MAP: - { - throw new NotImplementedException( - "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072"); - } + { + throw new NotImplementedException( + "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072"); + } case OnnxValueType.ONNX_TYPE_OPTIONAL: - { - var opt = Onnx.OptionalProto.Parser.ParseFrom(file); - return new DisposableTestPair(nodeName, CreateOrtValueFromOptional(opt, nodeName, nodeMeta)); - } + { + var opt = Onnx.OptionalProto.Parser.ParseFrom(file); + return new DisposableTestPair(nodeName, CreateOrtValueFromOptional(opt, nodeName, nodeMeta)); + } default: throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented"); } @@ -279,14 +281,14 @@ namespace Microsoft.ML.OnnxRuntime.Tests } private static void SequenceCheckMatchOnnxType(string nodeName, SequenceMetadata meta, - OnnxValueType onnxType) + OnnxValueType onnxType) { if (meta.ElementMeta.OnnxValueType == onnxType) return; throw new InvalidDataException($"Sequence node: '{nodeName}' " + - $"has element type: '{onnxType}'" + - $" expected: '{meta.ElementMeta.OnnxValueType}'"); + $"has element type: '{onnxType}'" + + $" expected: '{meta.ElementMeta.OnnxValueType}'"); } private static string MakeSequenceElementName(string nodeName, string seqName, int seqNum) @@ -307,55 +309,54 @@ namespace Microsoft.ML.OnnxRuntime.Tests switch (seqElemType) { case Onnx.SequenceProto.Types.DataType.Tensor: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR); + var sequenceOfTensors = new List(sequence.TensorValues.Count); + foreach (var tensor in sequence.TensorValues) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR); - var sequenceOfTensors = new List(sequence.TensorValues.Count); - foreach (var tensor in sequence.TensorValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - var namedOnnxValue = LoadTensorPb(tensor, elemName, elemMeta); - sequenceOfTensors.Add(namedOnnxValue); - } - return NamedOnnxValue.CreateFromSequence(nodeName, sequenceOfTensors); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + var namedOnnxValue = LoadTensorPb(tensor, elemName, elemMeta); + sequenceOfTensors.Add(namedOnnxValue); } + return NamedOnnxValue.CreateFromSequence(nodeName, sequenceOfTensors); + } case Onnx.SequenceProto.Types.DataType.Sequence: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE); + var seqOfSequences = new List(sequence.SequenceValues.Count); + foreach (var s in sequence.SequenceValues) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE); - var seqOfSequences = new List(sequence.SequenceValues.Count); - foreach (var s in sequence.SequenceValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - seqOfSequences.Add(CreateNamedOnnxValueFromSequence(s, elemName, elemMeta)); - } - return NamedOnnxValue.CreateFromSequence(nodeName, seqOfSequences); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + seqOfSequences.Add(CreateNamedOnnxValueFromSequence(s, elemName, elemMeta)); } + return NamedOnnxValue.CreateFromSequence(nodeName, seqOfSequences); + } case Onnx.SequenceProto.Types.DataType.Map: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_MAP); + var seqOfMaps = new List(sequence.MapValues.Count); + foreach (var m in sequence.MapValues) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_MAP); - var seqOfMaps = new List(sequence.MapValues.Count); - foreach (var m in sequence.MapValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - seqOfMaps.Add(CreateNamedOnnxValueFromMap(m, elemName, elemMeta)); - } - return NamedOnnxValue.CreateFromSequence(nodeName, seqOfMaps); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + seqOfMaps.Add(CreateNamedOnnxValueFromMap(m, elemName, elemMeta)); } + return NamedOnnxValue.CreateFromSequence(nodeName, seqOfMaps); + } case Onnx.SequenceProto.Types.DataType.Optional: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL); + var seqOfOpts = new List(sequence.OptionalValues.Count); + foreach (var opt in sequence.OptionalValues) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL); - var seqOfOpts = new List(sequence.OptionalValues.Count); - foreach (var opt in sequence.OptionalValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - seqOfOpts.Add(CreateNamedOnnxValueFromOptional(opt, elemName, elemMeta)); - } - return NamedOnnxValue.CreateFromSequence(nodeName, seqOfOpts); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + seqOfOpts.Add(CreateNamedOnnxValueFromOptional(opt, elemName, elemMeta)); } + return NamedOnnxValue.CreateFromSequence(nodeName, seqOfOpts); + } default: throw new NotImplementedException($"Sequence test data loading does not support element type: " + - $"'{seqElemType}'"); + $"'{seqElemType}'"); } - } internal static NamedOnnxValue CreateNamedOnnxValueFromMap(Onnx.MapProto map, string nodeName, NodeMetadata nodeMetadata) { @@ -369,20 +370,20 @@ namespace Microsoft.ML.OnnxRuntime.Tests switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType) { case Onnx.OptionalProto.Types.DataType.Tensor: - { - var tensor = optional.TensorValue; - return LoadTensorPb(tensor, nodeName, meta); - } + { + var tensor = optional.TensorValue; + return LoadTensorPb(tensor, nodeName, meta); + } case Onnx.OptionalProto.Types.DataType.Sequence: - { - var sequence = optional.SequenceValue; - return CreateNamedOnnxValueFromSequence(sequence, nodeName, meta); - } + { + var sequence = optional.SequenceValue; + return CreateNamedOnnxValueFromSequence(sequence, nodeName, meta); + } case Onnx.OptionalProto.Types.DataType.Map: - { - var map = optional.MapValue; - return CreateNamedOnnxValueFromMap(map, nodeName, meta); - } + { + var map = optional.MapValue; + return CreateNamedOnnxValueFromMap(map, nodeName, meta); + } case Onnx.OptionalProto.Types.DataType.Optional: throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional"); default: @@ -395,7 +396,8 @@ namespace Microsoft.ML.OnnxRuntime.Tests } internal static NamedOnnxValue CreateNamedOnnxValueFromRawData(string name, ReadOnlySpan rawData, - int[] dimensions) where T : struct + int[] dimensions) + where T : struct { var typedSrcSpan = MemoryMarshal.Cast(rawData); var dt = new DenseTensor(typedSrcSpan.ToArray(), dimensions); @@ -407,14 +409,14 @@ namespace Microsoft.ML.OnnxRuntime.Tests if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR) { throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" + - $" but loading as tensor: {tensor.Name}"); + $" but loading as tensor: {tensor.Name}"); } var protoDt = (Tensors.TensorElementType)tensor.DataType; var metaElementType = nodeMeta.ElementDataType; if (!((protoDt == metaElementType) || - (protoDt == TensorElementType.UInt16 && - (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16)))) + (protoDt == TensorElementType.UInt16 && + (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16)))) throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'"); // Tensors within Sequences may have no dimensions as the standard allows @@ -422,7 +424,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count) { throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " + - $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}"); + $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}"); } var shape = tensor.Dims.ToArray(); @@ -431,7 +433,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests { if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != shape[i])) throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " + - $"is expected to either be -1 or {shape[i]}"); + $"is expected to either be -1 or {shape[i]}"); } // element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so @@ -452,56 +454,55 @@ namespace Microsoft.ML.OnnxRuntime.Tests switch (seqElemType) { case Onnx.SequenceProto.Types.DataType.Tensor: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR); + using (var sequenceOfTensors = new DisposableListTest(sequence.TensorValues.Count)) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR); - using (var sequenceOfTensors = new DisposableListTest(sequence.TensorValues.Count)) + foreach (var tensor in sequence.TensorValues) { - foreach (var tensor in sequence.TensorValues) - { - var element = LoadOrValueTensorPb(tensor, sequence.Name, elemMeta); - sequenceOfTensors.Add(element); - } - return OrtValue.CreateSequence(sequenceOfTensors); + var element = LoadOrValueTensorPb(tensor, sequence.Name, elemMeta); + sequenceOfTensors.Add(element); } + return OrtValue.CreateSequence(sequenceOfTensors); } + } case Onnx.SequenceProto.Types.DataType.Sequence: // Sequence of sequences + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE); + using (var seqOfSequences = new DisposableListTest(sequence.TensorValues.Count)) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE); - using (var seqOfSequences = new DisposableListTest(sequence.TensorValues.Count)) + foreach (var s in sequence.SequenceValues) { - foreach (var s in sequence.SequenceValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - var ortValue = CreateOrtValueFromSequence(s, elemName, elemMeta); - seqOfSequences.Add(ortValue); - } - return OrtValue.CreateSequence(seqOfSequences); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + var ortValue = CreateOrtValueFromSequence(s, elemName, elemMeta); + seqOfSequences.Add(ortValue); } + return OrtValue.CreateSequence(seqOfSequences); } + } case Onnx.SequenceProto.Types.DataType.Map: - { - throw new NotImplementedException( - "Test data format for maps is under investigation"); - } + { + throw new NotImplementedException( + "Test data format for maps is under investigation"); + } case Onnx.SequenceProto.Types.DataType.Optional: + { + SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL); + using (var seqOfSequences = new DisposableListTest(sequence.TensorValues.Count)) { - SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL); - using (var seqOfSequences = new DisposableListTest(sequence.TensorValues.Count)) + foreach (var opt in sequence.OptionalValues) { - foreach (var opt in sequence.OptionalValues) - { - var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); - var ortValue = CreateOrtValueFromOptional(opt, elemName, elemMeta); - seqOfSequences.Add(ortValue); - } - return OrtValue.CreateSequence(seqOfSequences); + var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++); + var ortValue = CreateOrtValueFromOptional(opt, elemName, elemMeta); + seqOfSequences.Add(ortValue); } + return OrtValue.CreateSequence(seqOfSequences); } + } default: throw new NotImplementedException($"Sequence test data loading does not support element type: " + - $"'{seqElemType}'"); + $"'{seqElemType}'"); } - } internal static OrtValue CreateOrtValueFromOptional(Onnx.OptionalProto optional, string nodeName, NodeMetadata nodeMetadata) @@ -510,21 +511,20 @@ namespace Microsoft.ML.OnnxRuntime.Tests switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType) { case Onnx.OptionalProto.Types.DataType.Tensor: - { - var tensor = optional.TensorValue; - return LoadOrValueTensorPb(tensor, nodeName, meta); - } + { + var tensor = optional.TensorValue; + return LoadOrValueTensorPb(tensor, nodeName, meta); + } case Onnx.OptionalProto.Types.DataType.Sequence: - { - var sequence = optional.SequenceValue; - return CreateOrtValueFromSequence(sequence, nodeName, meta); - } + { + var sequence = optional.SequenceValue; + return CreateOrtValueFromSequence(sequence, nodeName, meta); + } case Onnx.OptionalProto.Types.DataType.Map: - { - throw new NotImplementedException( - "Test data format for maps is under investigation"); - - } + { + throw new NotImplementedException( + "Test data format for maps is under investigation"); + } case Onnx.OptionalProto.Types.DataType.Optional: throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional"); default: @@ -566,7 +566,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests } internal static NamedOnnxValue CreateNamedOnnxValueFromStringTensor(IList strings, - string nodeName, int[] dimensions) + string nodeName, int[] dimensions) { string[] strArray = new string[strings.Count]; for (int i = 0; i < strings.Count; ++i) @@ -582,7 +582,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests return NamedOnnxValue.CreateFromTensor(nodeName, dt); } internal static OrtValue CreateOrtValueFromStringTensor(IList strings, - long[] shape) + long[] shape) { var ortValue = OrtValue.CreateTensorWithEmptyStrings(OrtAllocator.DefaultInstance, shape); try @@ -608,7 +608,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests using (var inputFile = new System.IO.StreamReader(filename)) { if (skipheader) - inputFile.ReadLine(); //skip the input name + inputFile.ReadLine(); // skip the input name string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']', ' ' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < dataStr.Length; i++) { @@ -619,78 +619,4 @@ namespace Microsoft.ML.OnnxRuntime.Tests return tensorData.ToArray(); } } - - internal class FloatComparer : IEqualityComparer - { - private float atol = 1e-3f; - private float rtol = 1.7e-2f; - - public bool Equals(float x, float y) - { - return Math.Abs(x - y) <= (atol + rtol * Math.Abs(y)); - } - public int GetHashCode(float x) - { - return x.GetHashCode(); - } - } - - internal class DoubleComparer : IEqualityComparer - { - private double atol = 1e-3; - private double rtol = 1.7e-2; - - public bool Equals(double x, double y) - { - return Math.Abs(x - y) <= (atol + rtol * Math.Abs(y)); - } - public int GetHashCode(double x) - { - return x.GetHashCode(); - } - } - - class ExactComparer : IEqualityComparer - { - public bool Equals(T x, T y) - { - return x.Equals(y); - } - public int GetHashCode(T x) - { - return x.GetHashCode(); - } - } - - /// - /// Use it to compare Float16 - /// - internal class Float16Comparer : IEqualityComparer - { - public ushort tolerance = 0; - public bool Equals(Float16 x, Float16 y) - { - return Math.Abs(x.value - y.value) <= (tolerance + y.value); - } - public int GetHashCode(Float16 x) - { - return x.GetHashCode(); - } - } - - /// - /// Use it to compare Bloat16 - /// - internal class BFloat16Comparer : IEqualityComparer - { - public ushort tolerance = 0; - public bool Equals(BFloat16 x, BFloat16 y) - { - return Math.Abs(x.value - y.value) <= (tolerance + y.value); - } - public int GetHashCode(BFloat16 x) - { - return x.GetHashCode(); - } - } -} \ No newline at end of file +} diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj index 2cbb617fe8..61c3b1079f 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj @@ -119,30 +119,8 @@ - - OrtFloat16Tests.cs - - - OrtEnvTests.cs - - - InferenceTest.cs - - - OrtIoBindingAllocationTest.cs - - - TensorTests.cs - - - OrtValueTests.cs - - - ArrayTensorExtensionsTests.cs - - - TrainingTest.cs - + + diff --git a/csharp/tools/MauiModelTester/App.xaml b/csharp/tools/MauiModelTester/App.xaml new file mode 100644 index 0000000000..43db05cabf --- /dev/null +++ b/csharp/tools/MauiModelTester/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/csharp/tools/MauiModelTester/App.xaml.cs b/csharp/tools/MauiModelTester/App.xaml.cs new file mode 100644 index 0000000000..6fce0251cf --- /dev/null +++ b/csharp/tools/MauiModelTester/App.xaml.cs @@ -0,0 +1,11 @@ +namespace MauiModelTester; + +public partial class App : Application +{ + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } +} diff --git a/csharp/tools/MauiModelTester/AppShell.xaml b/csharp/tools/MauiModelTester/AppShell.xaml new file mode 100644 index 0000000000..986fcf907d --- /dev/null +++ b/csharp/tools/MauiModelTester/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/csharp/tools/MauiModelTester/AppShell.xaml.cs b/csharp/tools/MauiModelTester/AppShell.xaml.cs new file mode 100644 index 0000000000..15b1485511 --- /dev/null +++ b/csharp/tools/MauiModelTester/AppShell.xaml.cs @@ -0,0 +1,9 @@ +namespace MauiModelTester; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} diff --git a/csharp/tools/MauiModelTester/MainPage.xaml b/csharp/tools/MauiModelTester/MainPage.xaml new file mode 100644 index 0000000000..3e34fa67d8 --- /dev/null +++ b/csharp/tools/MauiModelTester/MainPage.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + +