2023-06-29 15:59:23 +00:00
using Microsoft.ML.OnnxRuntime.Tensors ;
using System ;
2021-10-27 10:07:07 +00:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices ;
2023-04-11 16:41:59 +00:00
using System.Text.RegularExpressions ;
2021-10-27 10:07:07 +00:00
using Xunit ;
2025-01-27 18:58:38 +00:00
#if NET8_0_OR_GREATER
using SystemNumericsTensors = System . Numerics . Tensors ;
#endif
2021-10-27 10:07:07 +00:00
namespace Microsoft.ML.OnnxRuntime.Tests
{
2023-04-11 16:41:59 +00:00
/// <summary>
/// This is compensate for the absence of string.Contains() in .NET Standard 2.0
/// Contains(String, StringComparison)
/// </summary>
public static class StringExtensions
2021-10-27 10:07:07 +00:00
{
2023-04-11 16:41:59 +00:00
public static bool Contains ( this String str , String substring ,
StringComparison comp )
{
if ( substring = = null )
throw new ArgumentNullException ( "substring" ,
"substring cannot be null." ) ;
else if ( ! Enum . IsDefined ( typeof ( StringComparison ) , comp ) )
throw new ArgumentException ( "comp is not a member of StringComparison" ,
"comp" ) ;
return str . IndexOf ( substring , comp ) > = 0 ;
}
2022-10-27 17:00:47 +00:00
}
2023-04-28 08:53:02 +00:00
2023-04-11 16:41:59 +00:00
public partial class InferenceTest
{
2021-10-27 10:07:07 +00:00
private const string module = "onnxruntime.dll" ;
private const string propertiesFile = "Properties.txt" ;
[Fact(DisplayName = "CanCreateAndDisposeSessionWithModelPath")]
public void CanCreateAndDisposeSessionWithModelPath ( )
{
string modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "squeezenet.onnx" ) ;
using ( var session = new InferenceSession ( modelPath ) )
{
Assert . NotNull ( session ) ;
Assert . NotNull ( session . InputMetadata ) ;
2024-09-03 00:08:29 +00:00
Assert . Single ( session . InputMetadata ) ; // 1 input nodeMeta
2023-04-11 16:41:59 +00:00
Assert . True ( session . InputMetadata . ContainsKey ( "data_0" ) ) ; // input nodeMeta name
2021-10-27 10:07:07 +00:00
Assert . Equal ( typeof ( float ) , session . InputMetadata [ "data_0" ] . ElementType ) ;
Assert . True ( session . InputMetadata [ "data_0" ] . IsTensor ) ;
var expectedInputDimensions = new int [ ] { 1 , 3 , 224 , 224 } ;
Assert . Equal ( expectedInputDimensions . Length , session . InputMetadata [ "data_0" ] . Dimensions . Length ) ;
for ( int i = 0 ; i < expectedInputDimensions . Length ; i + + )
{
Assert . Equal ( expectedInputDimensions [ i ] , session . InputMetadata [ "data_0" ] . Dimensions [ i ] ) ;
}
Assert . NotNull ( session . OutputMetadata ) ;
2024-09-03 00:08:29 +00:00
Assert . Single ( session . OutputMetadata ) ; // 1 output nodeMeta
2023-04-11 16:41:59 +00:00
Assert . True ( session . OutputMetadata . ContainsKey ( "softmaxout_1" ) ) ; // output nodeMeta name
2021-10-27 10:07:07 +00:00
Assert . Equal ( typeof ( float ) , session . OutputMetadata [ "softmaxout_1" ] . ElementType ) ;
Assert . True ( session . OutputMetadata [ "softmaxout_1" ] . IsTensor ) ;
var expectedOutputDimensions = new int [ ] { 1 , 1000 , 1 , 1 } ;
Assert . Equal ( expectedOutputDimensions . Length , session . OutputMetadata [ "softmaxout_1" ] . Dimensions . Length ) ;
for ( int i = 0 ; i < expectedOutputDimensions . Length ; i + + )
{
Assert . Equal ( expectedOutputDimensions [ i ] , session . OutputMetadata [ "softmaxout_1" ] . Dimensions [ i ] ) ;
}
}
}
2025-01-27 18:58:38 +00:00
#if NET8_0_OR_GREATER
#pragma warning disable SYSLIB5001 // System.Numerics.Tensors is only in preview so we can continue receiving API feedback
[Theory]
[InlineData(GraphOptimizationLevel.ORT_DISABLE_ALL, true)]
[InlineData(GraphOptimizationLevel.ORT_DISABLE_ALL, false)]
[InlineData(GraphOptimizationLevel.ORT_ENABLE_EXTENDED, true)]
[InlineData(GraphOptimizationLevel.ORT_ENABLE_EXTENDED, false)]
private void CanRunInferenceOnAModelDotnetTensors ( GraphOptimizationLevel graphOptimizationLevel , bool enableParallelExecution )
{
var model = TestDataLoader . LoadModelFromEmbeddedResource ( "squeezenet.onnx" ) ;
using ( var cleanUp = new DisposableListTest < IDisposable > ( ) )
{
// Set the graph optimization level for this session.
SessionOptions options = new SessionOptions ( ) ;
cleanUp . Add ( options ) ;
options . GraphOptimizationLevel = graphOptimizationLevel ;
var session = new InferenceSession ( model , options ) ;
cleanUp . Add ( session ) ;
using var runOptions = new RunOptions ( ) ;
var inputMeta = session . InputMetadata ;
var outputMeta = session . OutputMetadata ;
float [ ] expectedOutput = TestDataLoader . LoadTensorFromEmbeddedResource ( "bench.expected_out" ) ;
long [ ] expectedDimensions = { 1 , 1000 , 1 , 1 } ; // hardcoded for now for the test data
ReadOnlySpan < long > expectedOutputDimensions = expectedDimensions ;
float [ ] inputData = TestDataLoader . LoadTensorFromEmbeddedResource ( "bench.in" ) ; // this is the data for only one input tensor for this model
using var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) ;
foreach ( var name in inputMeta . Keys )
{
Assert . Equal ( typeof ( float ) , inputMeta [ name ] . ElementType ) ;
Assert . True ( inputMeta [ name ] . IsTensor ) ;
var tensor = SystemNumericsTensors . Tensor . Create < float > ( inputData , inputMeta [ name ] . Dimensions . Select ( x = > ( nint ) x ) . ToArray ( ) ) ;
inputOrtValues . Add ( new DisposableTestPair < OrtValue > ( name , OrtValue . CreateTensorValueFromSystemNumericsTensorObject < float > ( tensor ) ) ) ;
}
runOptions . LogId = "CsharpTest" ;
runOptions . Terminate = false ; // TODO: Test terminate = true, it currently crashes
runOptions . LogSeverityLevel = OrtLoggingLevel . ORT_LOGGING_LEVEL_ERROR ;
// Run inference with named inputs and outputs created with in Run()
using ( var results = session . Run ( runOptions , inputOrtValues . Select ( x = > x . Key ) . ToList ( ) , inputOrtValues . Select ( x = > x . Value ) . ToList ( ) , new List < string > ( [ "softmaxout_1" ] ) ) ) // results is an IDisposableReadOnlyCollection<OrtValue> container
{
// validate the results
foreach ( var r in results )
{
Assert . Single ( results ) ;
ValidateRunResult ( r , expectedOutput , expectedDimensions ) ;
}
}
}
}
[Fact]
public void InferenceSessionDisposedDotnetTensors ( )
{
var model = TestDataLoader . LoadModelFromEmbeddedResource ( "squeezenet.onnx" ) ;
// Set the graph optimization level for this session.
using ( SessionOptions options = new SessionOptions ( ) )
{
options . ProfileOutputPathPrefix = "Ort_P_" ;
options . EnableProfiling = true ;
using ( var session = new InferenceSession ( model , options ) )
{
var inputMeta = session . InputMetadata ;
var container = new List < NamedOnnxValue > ( ) ;
float [ ] inputData = TestDataLoader . LoadTensorFromEmbeddedResource ( "bench.in" ) ; // this is the data for only one input tensor for this model
using ( var runOptions = new RunOptions ( ) )
using ( var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) )
using ( var outputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . OutputMetadata . Count ) )
{
foreach ( var name in inputMeta . Keys )
{
Assert . Equal ( typeof ( float ) , inputMeta [ name ] . ElementType ) ;
Assert . True ( inputMeta [ name ] . IsTensor ) ;
var tensor = SystemNumericsTensors . Tensor . Create < float > ( inputData , inputMeta [ name ] . Dimensions . Select ( x = > ( nint ) x ) . ToArray ( ) ) ;
inputOrtValues . Add ( new DisposableTestPair < OrtValue > ( name , OrtValue . CreateTensorValueFromSystemNumericsTensorObject < float > ( tensor ) ) ) ;
}
// Run inference with named inputs and outputs created with in Run()
using ( var results = session . Run ( runOptions , inputOrtValues . Select ( x = > x . Key ) . ToList ( ) , inputOrtValues . Select ( x = > x . Value ) . ToList ( ) , new List < string > ( [ "softmaxout_1" ] ) ) ) // results is an IDisposableReadOnlyCollection<OrtValue> container
{
// validate the results
foreach ( var r in results )
{
Assert . Single ( results ) ;
float [ ] expectedOutput = TestDataLoader . LoadTensorFromEmbeddedResource ( "bench.expected_out" ) ;
long [ ] expectedDimensions = { 1 , 1000 , 1 , 1 } ; // hardcoded for now for the test data
ValidateRunResult ( r , expectedOutput , expectedDimensions ) ;
}
}
}
string profile_file = session . EndProfiling ( ) ;
// Profile file should have the output path prefix in it
Assert . Contains ( "Ort_P_" , profile_file ) ;
}
}
}
[Fact]
private void ThrowWrongOutputNameDotnetTensors ( )
{
var tuple = OpenSessionSqueezeNet ( ) ;
var session = tuple . Item1 ;
var inputData = tuple . Item2 ;
var inputTensor = tuple . Item3 ;
using ( var runOptions = new RunOptions ( ) )
using ( var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) )
using ( var outputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . OutputMetadata . Count ) )
{
var tensor = SystemNumericsTensors . Tensor . Create < float > ( inputData , Array . ConvertAll < int , nint > ( inputTensor . Dimensions . ToArray ( ) , x = > ( nint ) x ) ) ;
inputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "data_0" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject < float > ( tensor ) ) ) ;
outputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "bad_output_name" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject ( tensor ) ) ) ;
var ex = Assert . Throws < OnnxRuntimeException > ( ( ) = > session . Run ( runOptions , [ "data_0" ] , [ inputOrtValues [ 0 ] . Value ] , [ "bad_output_name" ] , [ outputOrtValues [ 0 ] . Value ] ) ) ;
Assert . Contains ( "Output name: 'bad_output_name' is not in the metadata" , ex . Message ) ;
}
session . Dispose ( ) ;
}
[Fact]
private void ThrowWrongOutputDimensionDotnetTensors ( )
{
var tuple = OpenSessionSqueezeNet ( ) ;
var session = tuple . Item1 ;
var inputData = tuple . Item2 ;
var inputTensor = tuple . Item3 ;
var outputTensor = SystemNumericsTensors . Tensor . Create < float > ( [ 1 , 1001 , 1 , 1 ] ) ;
using ( var runOptions = new RunOptions ( ) )
using ( var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) )
using ( var outputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . OutputMetadata . Count ) )
{
var tensor = SystemNumericsTensors . Tensor . Create < float > ( inputData , Array . ConvertAll < int , nint > ( inputTensor . Dimensions . ToArray ( ) , x = > ( nint ) x ) ) ;
inputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "data_0" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject < float > ( tensor ) ) ) ;
outputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "softmaxout_1" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject ( outputTensor ) ) ) ;
var ex = Assert . Throws < OnnxRuntimeException > ( ( ) = > session . Run ( runOptions , [ "data_0" ] , [ inputOrtValues [ 0 ] . Value ] , [ "softmaxout_1" ] , [ outputOrtValues [ 0 ] . Value ] ) ) ;
}
session . Dispose ( ) ;
}
[Fact]
private void ThrowInconsistentPinnedOutputsDotnetTensors ( )
{
var tuple = OpenSessionSqueezeNet ( ) ;
using var cleanUp = new DisposableListTest < IDisposable > ( ) ;
cleanUp . Add ( tuple . Item1 ) ;
var session = tuple . Item1 ;
var inputData = tuple . Item2 ;
var inputTensor = tuple . Item3 ;
var outputTensor = SystemNumericsTensors . Tensor . Create ( [ 1 , 1001 , 1 , 1 ] , [ 4 ] ) ;
using ( var runOptions = new RunOptions ( ) )
using ( var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) )
using ( var outputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . OutputMetadata . Count ) )
{
var tensor = SystemNumericsTensors . Tensor . Create < float > ( inputData , Array . ConvertAll < int , nint > ( inputTensor . Dimensions . ToArray ( ) , x = > ( nint ) x ) ) ;
inputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "data_0" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject < float > ( tensor ) ) ) ;
outputOrtValues . Add ( new DisposableTestPair < OrtValue > ( "softmaxout_1" , OrtValue . CreateTensorValueFromSystemNumericsTensorObject ( outputTensor ) ) ) ;
OrtValue [ ] outputs = [ ] ;
var ex = Assert . Throws < ArgumentException > ( ( ) = > session . Run ( runOptions , [ "data_0" ] , [ inputOrtValues [ 0 ] . Value ] , [ "softmaxout_1" ] , outputs ) ) ;
Assert . StartsWith ( "Length of outputNames (1) must match that of outputValues (0)." , ex . Message ) ;
}
}
#pragma warning restore SYSLIB5001 // System.Numerics.Tensors is only in preview so we can continue receiving API feedback
#endif
2022-01-06 19:03:14 +00:00
#if USE_CUDA
[Fact(DisplayName = "TestCUDAProviderOptions")]
private void TestCUDAProviderOptions ( )
{
string modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "squeezenet.onnx" ) ;
2023-08-26 03:30:45 +00:00
string defaultDeviceId = "0" ;
string deviceIdFromEnv = System . Environment . GetEnvironmentVariable ( "OnnxruntimeTestGpuDeviceId" ) ;
if ( ! string . IsNullOrEmpty ( deviceIdFromEnv ) & & int . TryParse ( deviceIdFromEnv , out int deviceId ) & & deviceId > = 0 )
{
defaultDeviceId = deviceIdFromEnv ;
output . WriteLine ( $"Parsed ID: {deviceIdFromEnv}" ) ;
}
2022-01-06 19:03:14 +00:00
using ( var cleanUp = new DisposableListTest < IDisposable > ( ) )
{
var cudaProviderOptions = new OrtCUDAProviderOptions ( ) ;
cleanUp . Add ( cudaProviderOptions ) ;
var providerOptionsDict = new Dictionary < string , string > ( ) ;
2023-08-26 03:30:45 +00:00
providerOptionsDict [ "device_id" ] = defaultDeviceId ;
2023-04-24 03:21:59 +00:00
// 256MB
providerOptionsDict [ "gpu_mem_limit" ] = "268435456" ;
2022-01-06 19:03:14 +00:00
providerOptionsDict [ "arena_extend_strategy" ] = "kSameAsRequested" ;
providerOptionsDict [ "cudnn_conv_algo_search" ] = "DEFAULT" ;
providerOptionsDict [ "do_copy_in_default_stream" ] = "1" ;
providerOptionsDict [ "cudnn_conv_use_max_workspace" ] = "1" ;
2022-03-28 23:31:36 +00:00
providerOptionsDict [ "cudnn_conv1d_pad_to_nc1d" ] = "1" ;
2022-01-06 19:03:14 +00:00
cudaProviderOptions . UpdateOptions ( providerOptionsDict ) ;
var resultProviderOptionsDict = new Dictionary < string , string > ( ) ;
ProviderOptionsValueHelper . StringToDict ( cudaProviderOptions . GetOptions ( ) , resultProviderOptionsDict ) ;
// test provider options configuration
string value ;
value = resultProviderOptionsDict [ "device_id" ] ;
Assert . Equal ( "0" , value ) ;
value = resultProviderOptionsDict [ "gpu_mem_limit" ] ;
2023-04-24 03:21:59 +00:00
Assert . Equal ( "268435456" , value ) ;
2022-01-06 19:03:14 +00:00
value = resultProviderOptionsDict [ "arena_extend_strategy" ] ;
Assert . Equal ( "kSameAsRequested" , value ) ;
value = resultProviderOptionsDict [ "cudnn_conv_algo_search" ] ;
Assert . Equal ( "DEFAULT" , value ) ;
value = resultProviderOptionsDict [ "do_copy_in_default_stream" ] ;
Assert . Equal ( "1" , value ) ;
value = resultProviderOptionsDict [ "cudnn_conv_use_max_workspace" ] ;
Assert . Equal ( "1" , value ) ;
2022-03-28 23:31:36 +00:00
value = resultProviderOptionsDict [ "cudnn_conv1d_pad_to_nc1d" ] ;
Assert . Equal ( "1" , value ) ;
2022-01-06 19:03:14 +00:00
// test correctness of provider options
SessionOptions options = SessionOptions . MakeSessionOptionWithCudaProvider ( cudaProviderOptions ) ;
cleanUp . Add ( options ) ;
var session = new InferenceSession ( modelPath , options ) ;
cleanUp . Add ( session ) ;
var inputMeta = session . InputMetadata ;
var container = new List < NamedOnnxValue > ( ) ;
float [ ] inputData = TestDataLoader . LoadTensorFromFile ( @"bench.in" ) ; // this is the data for only one input tensor for this model
foreach ( var name in inputMeta . Keys )
{
Assert . Equal ( typeof ( float ) , inputMeta [ name ] . ElementType ) ;
Assert . True ( inputMeta [ name ] . IsTensor ) ;
var tensor = new DenseTensor < float > ( inputData , inputMeta [ name ] . Dimensions ) ;
container . Add ( NamedOnnxValue . CreateFromTensor < float > ( name , tensor ) ) ;
}
session . Run ( container ) ;
}
}
#endif
2021-10-27 10:07:07 +00:00
#if USE_TENSORRT
[Fact(DisplayName = "CanRunInferenceOnAModelWithTensorRT")]
private void CanRunInferenceOnAModelWithTensorRT ( )
{
string modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "squeezenet.onnx" ) ;
2024-02-23 03:05:16 +00:00
2023-08-26 03:30:45 +00:00
int deviceId = 0 ;
string deviceIdStr = System . Environment . GetEnvironmentVariable ( "ONNXRUNTIME_TEST_GPU_DEVICE_ID" ) ;
if ( ! string . IsNullOrEmpty ( deviceIdStr ) & & int . TryParse ( deviceIdStr , out int parsedValue ) & & parsedValue > = 0 )
{
deviceId = parsedValue ;
output . WriteLine ( $"Parsed ID: {parsedValue}" ) ;
}
2021-10-27 10:07:07 +00:00
using ( var cleanUp = new DisposableListTest < IDisposable > ( ) )
{
2023-08-26 03:30:45 +00:00
SessionOptions options = SessionOptions . MakeSessionOptionWithTensorrtProvider ( deviceId ) ;
2021-10-27 10:07:07 +00:00
cleanUp . Add ( options ) ;
var session = new InferenceSession ( modelPath , options ) ;
cleanUp . Add ( session ) ;
var inputMeta = session . InputMetadata ;
var container = new List < NamedOnnxValue > ( ) ;
float [ ] inputData = TestDataLoader . LoadTensorFromFile ( @"bench.in" ) ; // this is the data for only one input tensor for this model
foreach ( var name in inputMeta . Keys )
{
Assert . Equal ( typeof ( float ) , inputMeta [ name ] . ElementType ) ;
Assert . True ( inputMeta [ name ] . IsTensor ) ;
var tensor = new DenseTensor < float > ( inputData , inputMeta [ name ] . Dimensions ) ;
container . Add ( NamedOnnxValue . CreateFromTensor < float > ( name , tensor ) ) ;
}
using ( var results = session . Run ( container ) )
{
ValidateRunResults ( results ) ;
}
}
}
[Fact(DisplayName = "TestTensorRTProviderOptions")]
private void TestTensorRTProviderOptions ( )
{
string modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "squeezenet.onnx" ) ;
string calTablePath = "squeezenet_calibration.flatbuffers" ;
string enginePath = "./" ;
string engineDecrptLibPath = "engine_decryp" ;
2023-08-26 03:30:45 +00:00
string defaultDeviceId = "0" ;
string deviceIdFromEnv = System . Environment . GetEnvironmentVariable ( "OnnxruntimeTestGpuDeviceId" ) ;
if ( ! string . IsNullOrEmpty ( deviceIdFromEnv ) & & int . TryParse ( deviceIdFromEnv , out int deviceId ) & & deviceId > = 0 )
{
defaultDeviceId = deviceIdFromEnv ;
output . WriteLine ( $"Parsed ID: {deviceIdFromEnv}" ) ;
}
2021-10-27 10:07:07 +00:00
using ( var cleanUp = new DisposableListTest < IDisposable > ( ) )
{
var trtProviderOptions = new OrtTensorRTProviderOptions ( ) ;
cleanUp . Add ( trtProviderOptions ) ;
var providerOptionsDict = new Dictionary < string , string > ( ) ;
2023-08-26 03:30:45 +00:00
providerOptionsDict [ "device_id" ] = defaultDeviceId ;
2021-10-27 10:07:07 +00:00
providerOptionsDict [ "trt_fp16_enable" ] = "1" ;
providerOptionsDict [ "trt_int8_enable" ] = "1" ;
providerOptionsDict [ "trt_int8_calibration_table_name" ] = calTablePath ;
providerOptionsDict [ "trt_engine_cache_enable" ] = "1" ;
providerOptionsDict [ "trt_engine_cache_path" ] = enginePath ;
providerOptionsDict [ "trt_engine_decryption_enable" ] = "0" ;
providerOptionsDict [ "trt_engine_decryption_lib_path" ] = engineDecrptLibPath ;
trtProviderOptions . UpdateOptions ( providerOptionsDict ) ;
var resultProviderOptionsDict = new Dictionary < string , string > ( ) ;
ProviderOptionsValueHelper . StringToDict ( trtProviderOptions . GetOptions ( ) , resultProviderOptionsDict ) ;
// test provider options configuration
string value ;
value = resultProviderOptionsDict [ "device_id" ] ;
2023-08-26 03:30:45 +00:00
Assert . Equal ( defaultDeviceId , value ) ;
2021-10-27 10:07:07 +00:00
value = resultProviderOptionsDict [ "trt_fp16_enable" ] ;
Assert . Equal ( "1" , value ) ;
value = resultProviderOptionsDict [ "trt_int8_enable" ] ;
Assert . Equal ( "1" , value ) ;
value = resultProviderOptionsDict [ "trt_int8_calibration_table_name" ] ;
Assert . Equal ( calTablePath , value ) ;
value = resultProviderOptionsDict [ "trt_engine_cache_enable" ] ;
Assert . Equal ( "1" , value ) ;
value = resultProviderOptionsDict [ "trt_engine_cache_path" ] ;
Assert . Equal ( enginePath , value ) ;
value = resultProviderOptionsDict [ "trt_engine_decryption_enable" ] ;
Assert . Equal ( "0" , value ) ;
value = resultProviderOptionsDict [ "trt_engine_decryption_lib_path" ] ;
Assert . Equal ( engineDecrptLibPath , value ) ;
// test correctness of provider options
SessionOptions options = SessionOptions . MakeSessionOptionWithTensorrtProvider ( trtProviderOptions ) ;
cleanUp . Add ( options ) ;
var session = new InferenceSession ( modelPath , options ) ;
cleanUp . Add ( session ) ;
var inputMeta = session . InputMetadata ;
var container = new List < NamedOnnxValue > ( ) ;
float [ ] inputData = TestDataLoader . LoadTensorFromFile ( @"bench.in" ) ; // this is the data for only one input tensor for this model
foreach ( var name in inputMeta . Keys )
{
Assert . Equal ( typeof ( float ) , inputMeta [ name ] . ElementType ) ;
Assert . True ( inputMeta [ name ] . IsTensor ) ;
var tensor = new DenseTensor < float > ( inputData , inputMeta [ name ] . Dimensions ) ;
container . Add ( NamedOnnxValue . CreateFromTensor < float > ( name , tensor ) ) ;
}
session . Run ( container ) ;
}
}
#endif
2022-10-12 05:53:58 +00:00
private static Func < DirectoryInfo , IEnumerable < DirectoryInfo > > getOpsetDirectories = delegate ( DirectoryInfo modelsDirInfo )
{
2022-10-14 17:22:05 +00:00
return modelsDirInfo . EnumerateDirectories ( "opset*" , SearchOption . AllDirectories ) ;
2022-10-12 05:53:58 +00:00
} ;
2021-10-27 10:07:07 +00:00
private static Dictionary < string , string > GetSkippedModels ( DirectoryInfo modelsDirInfo )
{
var skipModels = new Dictionary < string , string > ( ) {
{ "mxnet_arcface" , "Model is an invalid ONNX model" } ,
{ "tf_inception_v2" , "TODO: Debug failing model, skipping for now" } ,
{ "fp16_tiny_yolov2" , "Tolerance level for float16 is not known. We now support fp16." } ,
{ "fp16_test_tiny_yolov2" , "ImageScaler is not a registered function/op" } ,
{ "fp16_coreml_FNS-Candy" , "ImageScaler is not a registered function/op" } ,
{ "fp16_coreml_LinearRegression_NYCTaxi" , "Error in Node:featureVectorizer : No Op registered for FeatureVectorizer with domain_version of 1" } ,
{ "test_mnist" , "Does not run in opset9, runs in other opsets. The model runs but I don't have a data set to debug output locally. Tensors of type ElementType not currently supported in the LoadTensorFromFile" } ,
2023-04-11 16:41:59 +00:00
{ "BERT_Squad" , "Could not find an implementation for the nodeMeta bert / embeddings / one_hot:OneHot(9)" } ,
2021-10-27 10:07:07 +00:00
{ "mlperf_ssd_mobilenet_300" , "Could not find file output_0.pb" } ,
{ "tf_resnet_v1_50" , "result mismatch when Conv BN Fusion is applied" } ,
{ "tf_resnet_v1_101" , "result mismatch when Conv BN Fusion is applied" } ,
{ "tf_resnet_v1_152" , "result mismatch when Conv BN Fusion is applied" } ,
2022-10-12 05:53:58 +00:00
{ "cntk_simple_seg" , "Bad onnx test output caused by wrong SAME_UPPER/SAME_LOWER for ConvTranspose" } ,
2021-10-27 10:07:07 +00:00
{ "coreml_Imputer-LogisticRegression_sklearn_load_breast_cancer" , "Can't determine model file name" } ,
{ "mask_rcnn_keras" , "Model should be edited to remove the extra outputs" } ,
2023-04-11 16:41:59 +00:00
{ "test_maxunpool_export_with_output_shape" , "results mismatch" } ,
{ "test_min_int8" , "Could not find an implementation for Min(13) node with name" } ,
{ "test_min_uint8" , "Could not find an implementation for Min(13) node with name" } ,
{ "test_min_int16" , "Could not find an implementation for Min(13) node with name" } ,
{ "test_min_uint16" , "Could not find an implementation for Min(13) node with name" } ,
{ "test_max_int8" , "Could not find an implementation for Max(13) node with name" } ,
{ "test_max_uint8" , "Could not find an implementation for Max(13) node with name" } ,
{ "test_max_int16" , "Could not find an implementation for Max(13) node with name" } ,
{ "test_max_uint16" , "Could not find an implementation for Max(13) nodeMeta with name '" } ,
{ "test_mul_uint8" , "Could not find an implementation for Mul(14) node with name" } ,
{ "test_bitshift_right_uint16" , "Could not find an implementation for BitShift(11) nodeMeta with name ''" } ,
{ "test_bitshift_left_uint16" , "Could not find an implementation for BitShift(11)" } ,
{ "test_pow_types_float32_uint64" , "Could not find an implementation for Pow(15) node with name ''" } ,
{ "test_pow_types_float32_uint32" , "Could not find an implementation for Pow(15) node with name ''" } ,
{ "test_resize_downsample_scales_cubic_align_corners" , "Results mismatch" } ,
{ "test_resize_downsample_scales_linear_align_corners" , "Results mismatch" } ,
2021-10-27 10:07:07 +00:00
{ "test_gru_batchwise" , "batchwise operations not supported" } ,
2023-04-11 16:41:59 +00:00
{ "test_lstm_batchwise" , "Batchwise recurrent operations(layout == 1) are not supported.If you need support create a github issue with justification." } ,
2021-10-27 10:07:07 +00:00
{ "test_simple_rnn_batchwise" , "batchwise operations not supported" } ,
{ "test_batchnorm_example_training_mode" , "opset14 version not implemented yet" } ,
2023-04-11 16:41:59 +00:00
{ "test_bernoulli" , "random generator, results mismatch" } ,
{ "test_bernoulli_seed" , "random generator, results mismatch" } ,
{ "test_bernoulli_double" , "random generator, results mismatch" } ,
{ "test_bernoulli_expanded" , "random generator, results mismatch" } ,
{ "test_bernoulli_seed_expanded" , "random generator, results mismatch" } ,
{ "test_bernoulli_double_expanded" , "random generator, results mismatch" } ,
2023-02-20 10:19:10 +00:00
// the expansion of Softplus uses Exp(1). ORT has a Softplus kernel, so testing the expansion is
2023-01-23 22:04:27 +00:00
// unnecessary and fails as ORT support for Exp started at opset 6 (as ORT didn't exist until opset 7).
2023-05-02 01:35:26 +00:00
2023-04-11 16:41:59 +00:00
{ "test_clip_default_int8_max_expanded" , "Could not find an implementation for Less(13) nodeMeta with name ''" } ,
{ "test_softplus_expanded" , "Could not find an implementation for Exp(1) node with name ''" } ,
{ "test_softplus_example_expanded" , "Could not find an implementation for Exp(1) node with name ''" } ,
{ "test_div_uint8" , "Could not find an implementation for Div(14) nodeMeta with name ''" } ,
{ "test_add_uint8" , "Opset18 Could not find an implementation for Add(14) nodeMeta with name ''" } ,
{ "test_col2im_pads" , "Results mismatch due to a typo in test data" } ,
{ "test_optional_has_element_empty_optional_input" , "OptionalProto test metadata. Unable to load 'optional_input' optional element type of: Undefined type" } ,
{ "test_loop13_seq" , "3rd input is an empty sequence. Ort API does not tolerate empty seq: Number of values should be at least 1" } ,
// Training tests
{ "BERT-Squad-int8" , "training domain" } ,
{ "YOLOv3-12-int8" , "training_domain" } ,
{ "test_training_dropout_default" , "results mismatch" } ,
{ "test_training_dropout_default_mask" , "Results mismatch" } ,
{ "test_training_dropout" , "results mismatch" } ,
{ "test_training_dropout_mask" , "results mismatch." } ,
{ "test_momentum" , "ai.onnx.preview.training:Momentum(-1) is not a registered function/op" } ,
{ "test_momentum_multiple" , "ai.onnx.preview.training:Momentum(-1) is not a registered function/op" } ,
{ "test_nesterov_momentum" , "ai.onnx.preview.training:Momentum(-1) is not a registered function/op" } ,
{ "test_adam" , "ai.onnx.preview.training:Adam(-1) is not a registered function/op" } ,
{ "test_adam_multiple" , "ai.onnx.preview.training:Adam(-1) is not a registered function/op" } ,
{ "test_adagrad" , "ai.onnx.preview.training:Adagrad(-1) is not a registered function/op" } ,
{ "test_adagrad_multiple" , "ai.onnx.preview.training:Adagrad(-1) is not a registered function/op" } ,
2023-05-27 00:59:12 +00:00
{ "test_zfnet512" , "skip it as ZFNET-512" } ,
2021-10-27 10:07:07 +00:00
} ;
// The following models fails on nocontribops win CI
var disableContribOpsEnvVar = Environment . GetEnvironmentVariable ( "DisableContribOps" ) ;
var isContribOpsDisabled = ( disableContribOpsEnvVar ! = null ) ? disableContribOpsEnvVar . Equals ( "ON" ) : false ;
if ( isContribOpsDisabled )
{
skipModels [ "test_tiny_yolov2" ] = "Fails when ContribOps is disabled" ;
skipModels [ "mask_rcnn_keras" ] = "Pad is not a registered function/op" ;
}
// Skip traditional ML models
var disableMlOpsEnvVar = Environment . GetEnvironmentVariable ( "DisableMlOps" ) ;
var isMlOpsDisabled = ( disableMlOpsEnvVar ! = null ) ? disableMlOpsEnvVar . Equals ( "ON" ) : false ;
if ( isMlOpsDisabled )
{
2022-10-12 05:53:58 +00:00
foreach ( var opsetDir in getOpsetDirectories ( modelsDirInfo ) )
2021-10-27 10:07:07 +00:00
{
foreach ( var modelDir in opsetDir . EnumerateDirectories ( ) )
{
var modelDirName = modelDir . Name ;
if ( modelDirName . StartsWith ( "scikit_" ) | |
modelDirName . StartsWith ( "libsvm_" ) | |
modelDirName . StartsWith ( "coreml_" ) | |
modelDirName . StartsWith ( "keras2coreml_" ) | |
modelDirName . StartsWith ( "XGBoost_" ) )
{
skipModels [ modelDirName ] = "Fails when ML ops are disabled" ;
}
} //model
} //opset
}
// This model fails on x86 Win CI
if ( System . Environment . Is64BitProcess = = false )
{
skipModels [ "test_vgg19" ] = "Get preallocated buffer for initializer conv4_4_b_0 failed" ;
skipModels [ "GPT2_LM_HEAD" ] = "System out of memory" ;
skipModels [ "GPT2" ] = "System out of memory" ;
skipModels [ "test_GPT2" ] = "System out of memory" ;
skipModels [ "tf_pnasnet_large" ] = "Get preallocated buffer for initializer ConvBnFusion_BN_B_cell_5/comb_iter_1/left/bn_sep_7x7_1/beta:0_203 failed" ;
skipModels [ "tf_nasnet_large" ] = "Get preallocated buffer for initializer ConvBnFusion_BN_B_cell_11/beginning_bn/beta:0_331 failed" ;
2023-04-19 04:49:55 +00:00
skipModels [ "ZFNet-512" ] = "System out of memory" ;
2021-10-27 10:07:07 +00:00
skipModels [ "test_bvlc_reference_caffenet" ] = "System out of memory" ;
skipModels [ "coreml_VGG16_ImageNet" ] = "System out of memory" ;
skipModels [ "test_ssd" ] = "System out of memory" ;
skipModels [ "roberta_sequence_classification" ] = "System out of memory" ;
2022-10-22 05:54:24 +00:00
// models from model zoo
skipModels [ "VGG 19" ] = "bad allocation" ;
skipModels [ "VGG 19-caffe2" ] = "bad allocation" ;
skipModels [ "VGG 19-bn" ] = "bad allocation" ;
skipModels [ "VGG 16" ] = "bad allocation" ;
skipModels [ "VGG 16-bn" ] = "bad allocation" ;
skipModels [ "VGG 16-fp32" ] = "bad allocation" ;
2021-10-27 10:07:07 +00:00
}
return skipModels ;
}
public static IEnumerable < object [ ] > GetModelsForTest ( )
{
var modelsDir = GetTestModelsDir ( ) ;
var modelsDirInfo = new DirectoryInfo ( modelsDir ) ;
var skipModels = GetSkippedModels ( modelsDirInfo ) ;
2022-10-12 05:53:58 +00:00
foreach ( var opsetDir in getOpsetDirectories ( modelsDirInfo ) )
2021-10-27 10:07:07 +00:00
{
//var modelRoot = new DirectoryInfo(Path.Combine(modelsDir, opsetDir.Name));
foreach ( var modelDir in opsetDir . EnumerateDirectories ( ) )
{
2022-12-01 04:33:20 +00:00
if ( ! ( skipModels . ContainsKey ( modelDir . Name ) | |
2022-10-27 17:00:47 +00:00
modelDir . Name . Contains ( "int8" , StringComparison . OrdinalIgnoreCase ) | |
modelDir . Name . Contains ( "qdq" , StringComparison . OrdinalIgnoreCase ) ) )
2021-10-27 10:07:07 +00:00
{
2022-10-12 05:53:58 +00:00
yield return new object [ ] { modelDir . Parent . FullName , modelDir . Name } ;
2021-10-27 10:07:07 +00:00
}
} //model
} //opset
}
public static IEnumerable < object [ ] > GetSkippedModelForTest ( )
{
var modelsDir = GetTestModelsDir ( ) ;
var modelsDirInfo = new DirectoryInfo ( modelsDir ) ;
var skipModels = GetSkippedModels ( modelsDirInfo ) ;
2022-10-12 05:53:58 +00:00
foreach ( var opsetDir in getOpsetDirectories ( modelsDirInfo ) )
2021-10-27 10:07:07 +00:00
{
2022-10-12 05:53:58 +00:00
foreach ( var modelDir in opsetDir . EnumerateDirectories ( ) )
2021-10-27 10:07:07 +00:00
{
2022-10-27 17:00:47 +00:00
if ( skipModels . ContainsKey ( modelDir . Name ) | |
modelDir . Name . Contains ( "int8" , StringComparison . OrdinalIgnoreCase ) | |
modelDir . Name . Contains ( "qdq" , StringComparison . OrdinalIgnoreCase ) )
2021-10-27 10:07:07 +00:00
{
//Console.WriteLine("Model {0} is skipped due to the error: {1}", modelDir.FullName, skipModels[modelDir.Name]);
2022-10-12 05:53:58 +00:00
yield return new object [ ] { modelDir . Parent . FullName , modelDir . Name } ;
2021-10-27 10:07:07 +00:00
}
}
}
}
2023-04-11 16:41:59 +00:00
private string MatchInputOutputWithFile ( string fileName , InferenceSession session , bool input , out NodeMetadata result )
{
string nodeName = string . Empty ;
result = null ;
var names = ( input ) ? session . InputNames : session . OutputNames ;
var metadata = ( input ) ? session . InputMetadata : session . OutputMetadata ;
string regEx = ( input ) ? @"input_(\d{1,}).pb" : @"output_(\d{1,}).pb" ;
var inpOut = ( input ) ? "input" : "output" ;
// Extract the number from the file name, if not try to match the input/output name with the name of the file.
try
{
// captures start at index 1
var group = Regex . Matches ( fileName , regEx ) . Single ( ) . Groups [ 1 ] ;
var num = int . Parse ( group . Value ) ;
if ( num > = 0 & & num < names . Count )
{
nodeName = names [ num ] ;
result = metadata [ nodeName ] ;
}
else
{
throw new InvalidDataException ( $"Filename '{fileName}' {inpOut} number '{num}' is out of range for '{names.Count}' {inpOut}(s)" ) ;
}
}
catch ( Exception )
{
// Either does not match or can not parse the number
}
if ( result is null )
{
throw new InvalidDataException ( $"Unable to match file: {fileName} to input/output metadata" ) ;
}
return nodeName ;
}
// The numbering of the input files does not match the order of outputs
// listed in the metadata of test_BERT_Squad. Model metadata order:
// "unique_ids_raw_output___9:0", "segment_ids:0", "input_mask:0", "input_ids:0"
// The corr input files are: input_0.pb, input_3.pb, input_2.pb, input_1.pb
// Everything in reverse, but the 0.
// Previously, it worked because our test data has matching
// tensor names that we could match to metadata after we load the tensor.
// But now, we need to know ahead of time what Onnx type we load, and thus match
// metadata with the test data file before loading. Protobuf can happily load whatever
// and give you garbage.
private string MatchBertSquadInputs ( string fileName )
{
string nodeName = string . Empty ;
switch ( fileName )
{
case "input_0.pb" :
nodeName = "unique_ids_raw_output___9:0" ;
break ;
case "input_1.pb" :
nodeName = "input_ids:0" ;
break ;
case "input_2.pb" :
nodeName = "input_mask:0" ;
break ;
case "input_3.pb" :
nodeName = "segment_ids:0" ;
break ;
default :
throw new InvalidDataException ( $"Unhandled input file name: '{fileName}' for test_BERT_Squad" ) ;
}
return nodeName ;
}
// The model actually has only 3 outputs, but the Zoo version has 4 files are supplied.
// The numbering of the output files does not match the order of outputs
// listed in the metadata.
// Previously, it worked because our CI test data version has matching
// tensor names that we could match to metadata after we load the tensor.
// But now, we need to know ahead of time what Onnx type we load, and thus match
// metadata with the test data file before loading. Protobuf can happily load whatever
// and give you garbage.
// Order in the metadata: unstack:1, unstack:0, unique_ids:0
// The files are in reverse order
private string MatchBertSquadOutputs ( string fileName )
{
string nodeName = string . Empty ;
switch ( fileName )
{
case "output_0.pb" : // Int64
nodeName = "unique_ids:0" ;
break ;
case "output_1.pb" :
nodeName = "unstack:0" ;
break ;
case "output_2.pb" :
nodeName = "unstack:1" ;
break ;
default :
throw new InvalidDataException ( $"Unhandled output file name: '{fileName}' for test_BERT_Squad" ) ;
}
return nodeName ;
}
2023-06-29 15:59:23 +00:00
private const string keras_prelu_ImageNet_small_nodeName_Input = "p_re_lu_3_input" ;
private const string keras_prelu_ImageNet_small_nodeName_Output = "p_re_lu_3/add:0" ;
private void LoadInputData < T > ( string opset , string modelName ,
DirectoryInfo testDataDir ,
InferenceSession session ,
IList < T > inputContainer ,
Func < string , string , NodeMetadata , T > loader )
{
var inMeta = session . InputMetadata ;
foreach ( var f in testDataDir . EnumerateFiles ( "input_*.pb" ) )
{
if ( modelName = = "keras_prelu_ImageNet_small" & & opset = = "opset9" )
{
// The model has 1 input, match all file names (they are different in each data set)
// to the same input
var nodeName = keras_prelu_ImageNet_small_nodeName_Input ;
var nodeMeta = inMeta [ nodeName ] ;
inputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
else if ( modelName = = "test_BERT_Squad" & & opset = = "opset8" )
{
string nodeName = MatchBertSquadInputs ( f . Name ) ;
var nodeMeta = inMeta [ nodeName ] ;
inputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
else
{
var nodeName = MatchInputOutputWithFile ( f . Name , session , true , out NodeMetadata nodeMeta ) ;
inputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
}
}
private void LoadOutputData < T > ( string opset , string modelName ,
DirectoryInfo testDataDir ,
InferenceSession session ,
IList < T > outputContainer ,
Func < string , string , NodeMetadata , T > loader )
{
var outMeta = session . OutputMetadata ;
foreach ( var f in testDataDir . EnumerateFiles ( "output_*.pb" ) )
{
if ( modelName = = "keras_prelu_ImageNet_small" & & opset = = "opset9" )
{
// The model has 1 output, match all file names (they are different in each data set)
// to the same output
var nodeName = keras_prelu_ImageNet_small_nodeName_Output ;
var nodeMeta = outMeta [ nodeName ] ;
outputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
else if ( modelName = = "test_BERT_Squad" & & opset = = "opset8" )
{
string nodeName = MatchBertSquadOutputs ( f . Name ) ;
var nodeMeta = outMeta [ nodeName ] ;
outputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
else
{
// Otherwise, just match trailing filename number to the input name -> metadata
var nodeName = MatchInputOutputWithFile ( f . Name , session , false , out NodeMetadata nodeMeta ) ;
outputContainer . Add ( loader ( f . FullName , nodeName , nodeMeta ) ) ;
}
}
}
private void RunPretrainedModel ( InferenceSession session ,
IReadOnlyList < NamedOnnxValue > inputContainer , IReadOnlyList < NamedOnnxValue > outputContainer )
{
var outMeta = session . OutputMetadata ;
var orderedOutputNames = new List < string > ( outputContainer . Count ) ;
foreach ( var output in outputContainer )
{
orderedOutputNames . Add ( output . Name ) ;
}
using ( var resultCollection = session . Run ( inputContainer , orderedOutputNames ) )
{
Assert . Equal ( outputContainer . Count , resultCollection . Count ) ;
for ( int i = 0 ; i < resultCollection . Count ; + + i )
{
var result = resultCollection [ i ] ;
var outputValue = outputContainer [ i ] ;
Assert . NotNull ( outputValue ) ;
Assert . Equal ( result . Name , outputValue . Name ) ;
var outputMeta = outMeta [ outputValue . Name ] ;
if ( outputMeta . OnnxValueType = = OnnxValueType . ONNX_TYPE_OPTIONAL )
{
outputMeta = outputMeta . AsOptionalMetadata ( ) . ElementMeta ;
}
Assert . Equal ( outputValue . ValueType , outputMeta . OnnxValueType ) ;
switch ( outputValue . ValueType )
{
case OnnxValueType . ONNX_TYPE_TENSOR : // Only Dense tensors now
{
VerifyTensorResults ( outputMeta . ElementDataType , result , outputValue ) ;
}
break ;
case OnnxValueType . ONNX_TYPE_SEQUENCE :
{
VerifySequenceResults ( result , outputValue , outputMeta ) ;
}
break ;
default :
2024-09-03 00:08:29 +00:00
Assert . Fail ( $"TestPreTrainedModels cannot handle Onnxtype: {outputValue.ValueType}" ) ;
2023-06-29 15:59:23 +00:00
break ;
}
}
}
}
private void RunPretrainedModel ( InferenceSession session , RunOptions runOptions ,
IReadOnlyList < DisposableTestPair < OrtValue > > inputContainer ,
IReadOnlyList < DisposableTestPair < OrtValue > > outputContainer )
{
var outMeta = session . OutputMetadata ;
var orderedInputNames = new List < string > ( inputContainer . Count ) ;
var orderdedInputs = new List < OrtValue > ( inputContainer . Count ) ;
2024-10-02 17:00:43 +00:00
foreach ( var pair in inputContainer )
2023-06-29 15:59:23 +00:00
{
orderedInputNames . Add ( pair . Key ) ;
orderdedInputs . Add ( pair . Value ) ;
}
var orderedOutputNames = new List < string > ( outputContainer . Count ) ;
var orderedOutputs = new List < OrtValue > ( outputContainer . Count ) ;
foreach ( var pair in outputContainer )
{
orderedOutputNames . Add ( pair . Key ) ;
orderedOutputs . Add ( pair . Value ) ;
}
using ( var results = session . Run ( runOptions , orderedInputNames , orderdedInputs , orderedOutputNames ) )
{
Assert . Equal ( outMeta . Count , results . Count ) ;
Assert . Equal ( outputContainer . Count , results . Count ) ;
for ( int i = 0 ; i < outputContainer . Count ; + + i )
{
var resultValue = results [ i ] ;
var expectedValue = outputContainer [ i ] . Value ;
var outputMeta = outMeta [ orderedOutputNames [ i ] ] ;
if ( outputMeta . OnnxValueType = = OnnxValueType . ONNX_TYPE_OPTIONAL )
{
outputMeta = outputMeta . AsOptionalMetadata ( ) . ElementMeta ;
}
if ( outputMeta . OnnxValueType = = OnnxValueType . ONNX_TYPE_TENSOR )
{
VerifyTensorResults ( outputMeta . ElementDataType , resultValue , expectedValue ) ;
}
else if ( outputMeta . OnnxValueType = = OnnxValueType . ONNX_TYPE_SEQUENCE )
{
VerifySequenceResults ( resultValue , expectedValue , outputMeta ) ;
}
else
{
2024-09-03 00:08:29 +00:00
Assert . Fail ( $"TestPreTrainedModels cannot handle Onnxtype: {outputMeta.OnnxValueType}" ) ;
2023-06-29 15:59:23 +00:00
}
}
}
}
[Theory(DisplayName = "TestPretrainedModelsWithOrtValue")]
[MemberData(nameof(GetModelsForTest))]
[MemberData(nameof(GetSkippedModelForTest), Skip = "Skipped due to Error, please fix the error and enable the test")]
public void TestPretrainedModelsWithOrtValue ( string opsetDir , string modelName )
{
TestPreTrainedModels ( opsetDir , modelName , true ) ;
}
2021-10-27 10:07:07 +00:00
[Theory(DisplayName = "TestPreTrainedModels")]
[MemberData(nameof(GetModelsForTest))]
[MemberData(nameof(GetSkippedModelForTest), Skip = "Skipped due to Error, please fix the error and enable the test")]
2023-06-29 15:59:23 +00:00
private void TestPreTrainedModels ( string opsetDir , string modelName , bool useOrtValueAPIs = false )
2021-10-27 10:07:07 +00:00
{
2022-10-12 05:53:58 +00:00
var opsetDirInfo = new DirectoryInfo ( opsetDir ) ;
var opset = opsetDirInfo . Name ;
2021-10-27 10:07:07 +00:00
string onnxModelFileName = null ;
2022-10-12 05:53:58 +00:00
var modelDir = new DirectoryInfo ( Path . Combine ( opsetDir , modelName ) ) ;
2021-10-27 10:07:07 +00:00
try
{
var onnxModelNames = modelDir . GetFiles ( "*.onnx" ) ;
bool validModelFound = false ;
if ( onnxModelNames . Length > 0 )
{
// TODO remove file "._resnet34v2.onnx" from test set
for ( int i = 0 ; i < onnxModelNames . Length ; i + + )
{
if ( onnxModelNames [ i ] . Name ! = "._resnet34v2.onnx" )
{
onnxModelNames [ 0 ] = onnxModelNames [ i ] ;
validModelFound = true ;
}
}
}
if ( validModelFound )
{
onnxModelFileName = Path . Combine ( modelDir . FullName , onnxModelNames [ 0 ] . Name ) ;
}
else
{
var modelNamesList = string . Join ( "," , onnxModelNames . Select ( x = > x . ToString ( ) ) ) ;
throw new Exception ( $"Opset {opset} Model {modelName}. Can't determine model file name. Found these :{modelNamesList}" ) ;
}
2024-10-02 17:00:43 +00:00
using ( var runOptions = new RunOptions ( ) )
2021-10-27 10:07:07 +00:00
using ( var session = new InferenceSession ( onnxModelFileName ) )
{
string testDataDirNamePattern = "test_data*" ;
if ( opset = = "opset9" & & modelName = = "LSTM_Seq_lens_unpacked" )
{
testDataDirNamePattern = "seq_lens*" ; // discrepancy in data directory
}
foreach ( var testDataDir in modelDir . EnumerateDirectories ( testDataDirNamePattern ) )
{
2023-06-29 15:59:23 +00:00
if ( useOrtValueAPIs )
2021-10-27 10:07:07 +00:00
{
2023-06-29 15:59:23 +00:00
using ( var inputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . InputMetadata . Count ) )
using ( var outputOrtValues = new DisposableListTest < DisposableTestPair < OrtValue > > ( session . OutputMetadata . Count ) )
2023-04-11 16:41:59 +00:00
{
2023-06-29 15:59:23 +00:00
LoadInputData ( opset , modelName , testDataDir , session , inputOrtValues , TestDataLoader . LoadOrtValueFromFilePb ) ;
LoadOutputData ( opset , modelName , testDataDir , session , outputOrtValues , TestDataLoader . LoadOrtValueFromFilePb ) ;
RunPretrainedModel ( session , runOptions , inputOrtValues , outputOrtValues ) ;
2023-04-11 16:41:59 +00:00
}
2021-10-27 10:07:07 +00:00
}
2023-06-29 15:59:23 +00:00
else
2021-10-27 10:07:07 +00:00
{
2023-06-29 15:59:23 +00:00
var inputContainer = new List < NamedOnnxValue > ( session . InputMetadata . Count ) ;
LoadInputData ( opset , modelName , testDataDir , session , inputContainer , TestDataLoader . LoadOnnxValueFromFilePb ) ;
var outputContainer = new List < NamedOnnxValue > ( session . OutputMetadata . Count ) ;
LoadOutputData ( opset , modelName , testDataDir , session , outputContainer , TestDataLoader . LoadOnnxValueFromFilePb ) ;
RunPretrainedModel ( session , inputContainer , outputContainer ) ;
2021-10-27 10:07:07 +00:00
}
}
}
}
catch ( Exception ex )
{
var msg = $"Opset {opset}, Model {modelName}: ModelFile = {onnxModelFileName} error = {ex.Message}" ;
if ( ex . Message . Contains ( "ONNX Runtime only *guarantees* support for models stamped with official released onnx opset versions" ) )
{
// If the exception is thrown because the opset version of the test model is
// not supported by ONNXRuntime yet, then ignore the test and proceed.
// ORT allows commits from ONNX master and in such cases we do come across new opsets which are
// not supported in ORT yet. In order to force these tests to run set env var ALLOW_RELEASED_ONNX_OPSET_ONLY=0
output . WriteLine ( "Skipping the model test as the latest ONNX opset is not supported yet. Error Message: " + msg ) ;
}
else
{
throw new Exception ( msg + "\n" + ex . StackTrace ) ;
}
}
}
2023-06-29 15:59:23 +00:00
private static void VerifySequenceResults ( NamedOnnxValue result , NamedOnnxValue expectedValue , NodeMetadata metaData )
2023-04-11 16:41:59 +00:00
{
var meta = metaData . AsSequenceMetadata ( ) ;
var resultSequence = result . AsEnumerable < NamedOnnxValue > ( ) ;
var expectedSequence = expectedValue . AsEnumerable < NamedOnnxValue > ( ) ;
Assert . Equal ( resultSequence . Count ( ) , expectedSequence . Count ( ) ) ;
foreach ( var ( resultItem , expectedItem ) in resultSequence . Zip ( expectedSequence , ( r , e ) = > ( r , e ) ) )
{
Assert . Equal ( resultItem . ValueType , expectedItem . ValueType ) ;
Assert . Equal ( resultItem . ValueType , meta . ElementMeta . OnnxValueType ) ;
switch ( resultItem . ValueType )
{
case OnnxValueType . ONNX_TYPE_TENSOR :
VerifyTensorResults ( meta . ElementMeta . ElementDataType , resultItem , expectedItem ) ;
break ;
case OnnxValueType . ONNX_TYPE_SEQUENCE :
{
VerifySequenceResults ( resultItem , expectedItem , meta . ElementMeta ) ;
}
break ;
default :
2024-09-03 00:08:29 +00:00
Assert . Fail ( "VerifySequenceResults cannot handle Onnxtype: " + resultItem . ValueType . ToString ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
}
Assert . Equal ( resultItem . AsTensor < float > ( ) , expectedItem . AsTensor < float > ( ) , new FloatComparer ( ) ) ;
}
}
2023-06-29 15:59:23 +00:00
private static void VerifyTensorResults ( TensorElementType elementType , NamedOnnxValue result , NamedOnnxValue expectedValue )
2023-04-11 16:41:59 +00:00
{
switch ( elementType )
{
case TensorElementType . Float :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < float > ( ) , result . AsTensor < float > ( ) , new FloatComparer ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Double :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < double > ( ) , result . AsTensor < double > ( ) , new DoubleComparer ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Int32 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < int > ( ) , result . AsTensor < int > ( ) , new ExactComparer < int > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . UInt32 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < uint > ( ) , result . AsTensor < uint > ( ) , new ExactComparer < uint > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Int16 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < short > ( ) , result . AsTensor < short > ( ) , new ExactComparer < short > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . UInt16 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < ushort > ( ) , result . AsTensor < ushort > ( ) , new ExactComparer < ushort > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Int64 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < long > ( ) , result . AsTensor < long > ( ) , new ExactComparer < long > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . UInt64 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < ulong > ( ) , result . AsTensor < ulong > ( ) , new ExactComparer < ulong > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . UInt8 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < byte > ( ) , result . AsTensor < byte > ( ) , new ExactComparer < byte > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Int8 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( result . AsTensor < sbyte > ( ) , result . AsTensor < sbyte > ( ) , new ExactComparer < sbyte > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Bool :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < bool > ( ) , result . AsTensor < bool > ( ) , new ExactComparer < bool > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . Float16 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < Float16 > ( ) , result . AsTensor < Float16 > ( ) , new Float16Comparer { tolerance = 2 } ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . BFloat16 :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < BFloat16 > ( ) , result . AsTensor < BFloat16 > ( ) , new BFloat16Comparer { tolerance = 2 } ) ;
2023-04-11 16:41:59 +00:00
break ;
case TensorElementType . String :
2023-06-29 15:59:23 +00:00
Assert . Equal ( expectedValue . AsTensor < string > ( ) , result . AsTensor < string > ( ) , new ExactComparer < string > ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
default :
2024-09-03 00:08:29 +00:00
Assert . Fail ( "TestPreTrainedModels does not yet support output of type: " + elementType . ToString ( ) ) ;
2023-04-11 16:41:59 +00:00
break ;
}
}
2023-06-29 15:59:23 +00:00
private static void VerifySequenceResults ( OrtValue resultSequence , OrtValue expectedSequence , NodeMetadata metaData )
{
var allocator = OrtAllocator . DefaultInstance ;
Assert . Equal ( OnnxValueType . ONNX_TYPE_SEQUENCE , resultSequence . OnnxType ) ;
Assert . Equal ( OnnxValueType . ONNX_TYPE_SEQUENCE , expectedSequence . OnnxType ) ;
var elementMeta = metaData . AsSequenceMetadata ( ) . ElementMeta ;
var resultCount = resultSequence . GetValueCount ( ) ;
Assert . Equal ( expectedSequence . GetValueCount ( ) , resultCount ) ;
using ( var cleanUp = new DisposableListTest < IDisposable > ( ) )
{
for ( int i = 0 ; i < resultCount ; + + i )
{
var resultItem = resultSequence . GetValue ( i , allocator ) ;
cleanUp . Add ( resultItem ) ;
var expectedItem = expectedSequence . GetValue ( i , allocator ) ;
cleanUp . Add ( expectedItem ) ;
Assert . Equal ( elementMeta . OnnxValueType , expectedItem . OnnxType ) ;
Assert . Equal ( elementMeta . OnnxValueType , resultItem . OnnxType ) ;
switch ( elementMeta . OnnxValueType )
{
case OnnxValueType . ONNX_TYPE_TENSOR :
VerifyTensorResults ( elementMeta . ElementDataType , resultItem , expectedItem ) ;
break ;
case OnnxValueType . ONNX_TYPE_SEQUENCE :
{
VerifySequenceResults ( resultItem , expectedItem , elementMeta ) ;
}
break ;
default :
2024-09-03 00:08:29 +00:00
Assert . Fail ( $"VerifySequenceResults cannot handle Onnxtype: {elementMeta.OnnxValueType}" ) ;
2023-06-29 15:59:23 +00:00
break ;
}
}
}
}
private static void VerifyTensorResults ( TensorElementType expectedElementType , OrtValue result , OrtValue expectedValue )
{
Assert . True ( result . IsTensor ) ;
Assert . True ( expectedValue . IsTensor ) ;
var resultTypeShape = result . GetTensorTypeAndShape ( ) ;
var expectedTypeShape = expectedValue . GetTensorTypeAndShape ( ) ;
Assert . Equal ( expectedElementType , resultTypeShape . ElementDataType ) ;
Assert . Equal ( expectedElementType , expectedTypeShape . ElementDataType ) ;
Assert . Equal ( expectedTypeShape . Shape , resultTypeShape . Shape ) ;
if ( expectedElementType = = TensorElementType . String )
{
var resStrings = result . GetStringTensorAsArray ( ) ;
var expStrings = expectedValue . GetStringTensorAsArray ( ) ;
Assert . Equal ( expStrings , resStrings ) ;
return ;
}
switch ( expectedElementType )
{
case TensorElementType . Float :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < float > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < float > ( ) . ToArray ( ) ,
new FloatComparer ( ) ) ;
break ;
case TensorElementType . Double :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < double > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < double > ( ) . ToArray ( ) ,
new DoubleComparer ( ) ) ;
break ;
case TensorElementType . Int32 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < int > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < int > ( ) . ToArray ( ) , new ExactComparer < int > ( ) ) ;
break ;
case TensorElementType . UInt32 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < uint > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < uint > ( ) . ToArray ( ) , new ExactComparer < uint > ( ) ) ;
break ;
case TensorElementType . Int16 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < short > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < short > ( ) . ToArray ( ) , new ExactComparer < short > ( ) ) ;
break ;
case TensorElementType . UInt16 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < ushort > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < ushort > ( ) . ToArray ( ) , new ExactComparer < ushort > ( ) ) ;
break ;
case TensorElementType . Int64 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < long > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < long > ( ) . ToArray ( ) , new ExactComparer < long > ( ) ) ;
break ;
case TensorElementType . UInt64 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < ulong > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < ulong > ( ) . ToArray ( ) , new ExactComparer < ulong > ( ) ) ;
break ;
case TensorElementType . UInt8 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < byte > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < byte > ( ) . ToArray ( ) , new ExactComparer < byte > ( ) ) ;
break ;
case TensorElementType . Int8 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < sbyte > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < sbyte > ( ) . ToArray ( ) , new ExactComparer < sbyte > ( ) ) ;
break ;
case TensorElementType . Bool :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < bool > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < bool > ( ) . ToArray ( ) , new ExactComparer < bool > ( ) ) ;
break ;
case TensorElementType . Float16 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < Float16 > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < Float16 > ( ) . ToArray ( ) ,
new Float16Comparer { tolerance = 2 } ) ;
break ;
case TensorElementType . BFloat16 :
Assert . Equal ( expectedValue . GetTensorDataAsSpan < BFloat16 > ( ) . ToArray ( ) , result . GetTensorDataAsSpan < BFloat16 > ( ) . ToArray ( ) ,
new BFloat16Comparer { tolerance = 2 } ) ;
break ;
default :
2024-09-03 00:08:29 +00:00
Assert . Fail ( "VerifyTensorResults cannot handle ElementType: " + expectedElementType . ToString ( ) ) ;
2023-06-29 15:59:23 +00:00
break ;
}
}
private static void VerifyContainerContent ( IReadOnlyList < OrtValue > results ,
IReadOnlyList < NamedOnnxValue > expectedValues )
{
Assert . Equal ( results . Count , expectedValues . Count ) ;
for ( int i = 0 ; i < expectedValues . Count ; + + i )
{
var result = results [ i ] ;
var resultTypeShape = result . GetTensorTypeAndShape ( ) ;
var expectedValue = expectedValues [ i ] ;
Assert . Equal ( OnnxValueType . ONNX_TYPE_TENSOR , expectedValue . ValueType ) ;
switch ( resultTypeShape . ElementDataType )
{
case TensorElementType . Float :
Assert . Equal ( result . GetTensorDataAsSpan < float > ( ) . ToArray ( ) , expectedValue . AsTensor < float > ( ) . ToArray ( ) ,
new ExactComparer < float > ( ) ) ;
break ;
case TensorElementType . Double :
Assert . Equal ( result . GetTensorDataAsSpan < double > ( ) . ToArray ( ) , expectedValue . AsTensor < double > ( ) . ToArray ( ) ,
new DoubleComparer ( ) ) ;
break ;
case TensorElementType . Int32 :
Assert . Equal ( result . GetTensorDataAsSpan < int > ( ) . ToArray ( ) , expectedValue . AsTensor < int > ( ) . ToArray ( ) , new ExactComparer < int > ( ) ) ;
break ;
case TensorElementType . UInt32 :
Assert . Equal ( result . GetTensorDataAsSpan < uint > ( ) . ToArray ( ) , expectedValue . AsTensor < uint > ( ) . ToArray ( ) , new ExactComparer < uint > ( ) ) ;
break ;
case TensorElementType . Int16 :
Assert . Equal ( result . GetTensorDataAsSpan < short > ( ) . ToArray ( ) , expectedValue . AsTensor < short > ( ) . ToArray ( ) , new ExactComparer < short > ( ) ) ;
break ;
case TensorElementType . UInt16 :
Assert . Equal ( result . GetTensorDataAsSpan < ushort > ( ) . ToArray ( ) , expectedValue . AsTensor < ushort > ( ) . ToArray ( ) , new ExactComparer < ushort > ( ) ) ;
break ;
case TensorElementType . Int64 :
Assert . Equal ( result . GetTensorDataAsSpan < long > ( ) . ToArray ( ) , expectedValue . AsTensor < long > ( ) . ToArray ( ) , new ExactComparer < long > ( ) ) ;
break ;
case TensorElementType . UInt64 :
Assert . Equal ( result . GetTensorDataAsSpan < ulong > ( ) . ToArray ( ) , expectedValue . AsTensor < ulong > ( ) . ToArray ( ) , new ExactComparer < ulong > ( ) ) ;
break ;
case TensorElementType . UInt8 :
Assert . Equal ( result . GetTensorDataAsSpan < byte > ( ) . ToArray ( ) , expectedValue . AsTensor < byte > ( ) . ToArray ( ) , new ExactComparer < byte > ( ) ) ;
break ;
case TensorElementType . Int8 :
Assert . Equal ( result . GetTensorDataAsSpan < sbyte > ( ) . ToArray ( ) , expectedValue . AsTensor < sbyte > ( ) . ToArray ( ) , new ExactComparer < sbyte > ( ) ) ;
break ;
case TensorElementType . Bool :
Assert . Equal ( result . GetTensorDataAsSpan < bool > ( ) . ToArray ( ) , expectedValue . AsTensor < bool > ( ) . ToArray ( ) , new ExactComparer < bool > ( ) ) ;
break ;
case TensorElementType . Float16 :
Assert . Equal ( result . GetTensorDataAsSpan < Float16 > ( ) . ToArray ( ) , expectedValue . AsTensor < Float16 > ( ) . ToArray ( ) ,
new Float16Comparer { tolerance = 2 } ) ;
break ;
case TensorElementType . BFloat16 :
Assert . Equal ( result . GetTensorDataAsSpan < BFloat16 > ( ) . ToArray ( ) , expectedValue . AsTensor < BFloat16 > ( ) . ToArray ( ) ,
new BFloat16Comparer { tolerance = 2 } ) ;
break ;
case TensorElementType . String :
Assert . Equal ( result . GetStringTensorAsArray ( ) , expectedValue . AsTensor < string > ( ) . ToArray ( ) , new ExactComparer < string > ( ) ) ;
break ;
default :
2024-10-02 17:00:43 +00:00
Assert . Fail ( $"VerifyTensorResults cannot handle ElementType: {resultTypeShape.ElementDataType}" ) ;
2023-06-29 15:59:23 +00:00
break ;
}
}
}
2021-10-27 10:07:07 +00:00
// Hint: .NET Core 3.1 has a 'NativeLibrary' class that can be used to free the library handle
private void UnloadLibrary ( IntPtr libraryHandle )
{
if ( libraryHandle ! = IntPtr . Zero )
{
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
{
if ( ! FreeLibrary ( libraryHandle ) )
{
throw new Exception ( "Could not unload the provided shared library using its handle" ) ;
}
}
else
{
// TODO: Deal with non-Windows platforms for the .NET Core use-case
}
}
}
2023-04-28 08:53:02 +00:00
private string GetCustomOpLibFullPath ( )
{
string libName = "custom_op_library.dll" ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
{
libName = "custom_op_library.dll" ;
}
else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
{
libName = "libcustom_op_library.so" ;
}
else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
{
libName = "libcustom_op_library.dylib" ;
}
string libFullPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , libName ) ;
Assert . True ( File . Exists ( libFullPath ) , $"Expected lib {libFullPath} does not exist." ) ;
return libFullPath ;
}
2023-05-27 00:59:12 +00:00
private void ValidateModelWithCustomOps ( SessionOptions options )
2023-04-28 08:53:02 +00:00
{
string modelPath = "custom_op_test.onnx" ;
using ( var session = new InferenceSession ( modelPath , options ) )
{
var inputContainer = new List < NamedOnnxValue > ( ) ;
inputContainer . Add ( NamedOnnxValue . CreateFromTensor < float > ( "input_1" ,
new DenseTensor < float > (
new float [ ]
{
1.1f , 2.2f , 3.3f , 4.4f , 5.5f ,
6.6f , 7.7f , 8.8f , 9.9f , 10.0f ,
11.1f , 12.2f , 13.3f , 14.4f , 15.5f
} ,
new int [ ] { 3 , 5 }
) ) ) ;
inputContainer . Add ( NamedOnnxValue . CreateFromTensor < float > ( "input_2" ,
new DenseTensor < float > (
new float [ ]
{
15.5f , 14.4f , 13.3f , 12.2f , 11.1f ,
10.0f , 9.9f , 8.8f , 7.7f , 6.6f ,
5.5f , 4.4f , 3.3f , 2.2f , 1.1f
} ,
new int [ ] { 3 , 5 }
) ) ) ;
using ( var result = session . Run ( inputContainer ) )
{
Assert . Equal ( "output" , result . First ( ) . Name ) ;
var tensorOut = result . First ( ) . AsTensor < int > ( ) ;
var expectedOut = new DenseTensor < int > (
new int [ ]
{
17 , 17 , 17 , 17 , 17 ,
17 , 18 , 18 , 18 , 17 ,
17 , 17 , 17 , 17 , 17
} ,
new int [ ] { 3 , 5 }
) ;
Assert . True ( tensorOut . SequenceEqual ( expectedOut ) ) ;
}
}
}
2021-10-27 10:07:07 +00:00
[SkipNonPackageTests(DisplayName = "TestRegisterCustomOpLibrary")]
private void TestRegisterCustomOpLibrary ( )
{
using ( var option = new SessionOptions ( ) )
{
2023-04-28 08:53:02 +00:00
string libFullPath = GetCustomOpLibFullPath ( ) ;
try
2021-10-27 10:07:07 +00:00
{
2023-04-28 08:53:02 +00:00
option . RegisterCustomOpLibrary ( libFullPath ) ;
2021-10-27 10:07:07 +00:00
}
2023-04-28 08:53:02 +00:00
catch ( Exception ex )
2021-10-27 10:07:07 +00:00
{
2023-04-28 08:53:02 +00:00
var msg = $"Failed to load custom op library {libFullPath}, error = {ex.Message}" ;
throw new Exception ( msg + "\n" + ex . StackTrace ) ;
2021-10-27 10:07:07 +00:00
}
2023-04-28 08:53:02 +00:00
var ortEnvInstance = OrtEnv . Instance ( ) ;
string [ ] providers = ortEnvInstance . GetAvailableProviders ( ) ;
if ( Array . Exists ( providers , provider = > provider = = "CUDAExecutionProvider" ) )
2021-10-27 10:07:07 +00:00
{
2023-04-28 08:53:02 +00:00
option . AppendExecutionProvider_CUDA ( 0 ) ;
2021-10-27 10:07:07 +00:00
}
2023-04-28 08:53:02 +00:00
ValidateModelWithCustomOps ( option ) ;
}
}
[SkipNonPackageTests(DisplayName = "TestRegisterCustomOpLibraryV2")]
private void TestRegisterCustomOpLibraryV2 ( )
{
using ( var option = new SessionOptions ( ) )
{
string libFullPath = GetCustomOpLibFullPath ( ) ;
2021-10-27 10:07:07 +00:00
2022-01-05 17:22:15 +00:00
var ortEnvInstance = OrtEnv . Instance ( ) ;
string [ ] providers = ortEnvInstance . GetAvailableProviders ( ) ;
2023-04-11 16:41:59 +00:00
if ( Array . Exists ( providers , provider = > provider = = "CUDAExecutionProvider" ) )
{
2022-01-05 17:22:15 +00:00
option . AppendExecutionProvider_CUDA ( 0 ) ;
}
2021-10-27 10:07:07 +00:00
IntPtr libraryHandle = IntPtr . Zero ;
try
{
option . RegisterCustomOpLibraryV2 ( libFullPath , out libraryHandle ) ;
}
catch ( Exception ex )
{
var msg = $"Failed to load custom op library {libFullPath}, error = {ex.Message}" ;
throw new Exception ( msg + "\n" + ex . StackTrace ) ;
}
2023-04-28 08:53:02 +00:00
ValidateModelWithCustomOps ( option ) ;
2021-10-27 10:07:07 +00:00
2023-04-28 08:53:02 +00:00
// Safe to unload the custom op shared library now
UnloadLibrary ( libraryHandle ) ;
}
}
2021-10-27 10:07:07 +00:00
[Fact(DisplayName = "TestModelSerialization")]
private void TestModelSerialization ( )
{
string modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "squeezenet.onnx" ) ;
string modelOutputPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "optimized-squeezenet.onnx" ) ;
// Set the optimized model file path to assert that no exception are thrown.
using ( SessionOptions options = new SessionOptions ( ) )
{
options . OptimizedModelFilePath = modelOutputPath ;
options . GraphOptimizationLevel = GraphOptimizationLevel . ORT_ENABLE_BASIC ;
using ( var session = new InferenceSession ( modelPath , options ) )
{
Assert . NotNull ( session ) ;
Assert . True ( File . Exists ( modelOutputPath ) ) ;
}
}
}
2024-10-02 17:00:43 +00:00
private static OrtLoraAdapter CreateLoraAdapterFromFile ( )
{
var adapterPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "two_params_lora_model.onnx_adapter" ) ;
return OrtLoraAdapter . Create ( adapterPath , null ) ;
}
private static OrtLoraAdapter CreateLoraAdapterFromArray ( )
{
var adapterPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "two_params_lora_model.onnx_adapter" ) ;
var adapterBytes = File . ReadAllBytes ( adapterPath ) ;
return OrtLoraAdapter . Create ( adapterBytes , null ) ;
}
// See tests below for running with Lora Adapters
[Fact(DisplayName = "TestInferenceWithBaseLoraModel")]
private void TestInferenceWithBaseLoraModel ( )
{
var modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "two_params_lora_model.onnx" ) ;
var inputShape = new long [ ] { 4 , 4 } ;
var inputData = new float [ 16 ] ;
Array . Fill ( inputData , 1 ) ;
using var inputOrtValue = OrtValue . CreateTensorValueFromMemory ( inputData , inputShape ) ;
var expectedOutput = new float [ ] {
28 , 32 , 36 , 40 ,
28 , 32 , 36 , 40 ,
28 , 32 , 36 , 40 ,
28 , 32 , 36 , 40 } ;
using var session = new InferenceSession ( modelPath ) ;
using var runOptions = new RunOptions ( ) ;
using var outputs = session . Run ( runOptions , [ "input_x" ] , [ inputOrtValue ] , [ "output" ] ) ;
Assert . Single ( outputs ) ;
var output = outputs [ 0 ] . GetTensorDataAsSpan < float > ( ) ;
Assert . Equal ( expectedOutput . Length , output . Length ) ;
Assert . Equal ( expectedOutput , output . ToArray ( ) , new FloatComparer ( ) ) ;
}
private static void TestInferenceWithLoraAdapter ( OrtLoraAdapter ortLoraAdapter )
{
var modelPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "two_params_lora_model.onnx" ) ;
var adapterPath = Path . Combine ( Directory . GetCurrentDirectory ( ) , "two_params_lora_model.onnx_adapter" ) ;
var inputShape = new long [ ] { 4 , 4 } ;
var inputData = new float [ 16 ] ;
Array . Fill ( inputData , 1 ) ;
using var inputOrtValue = OrtValue . CreateTensorValueFromMemory ( inputData , inputShape ) ;
var expectedOutput = new float [ ] {
154 , 176 , 198 , 220 ,
154 , 176 , 198 , 220 ,
154 , 176 , 198 , 220 ,
154 , 176 , 198 , 220 } ;
using var session = new InferenceSession ( modelPath ) ;
using var runOptions = new RunOptions ( ) ;
runOptions . AddActiveLoraAdapter ( ortLoraAdapter ) ;
using var outputs = session . Run ( runOptions , [ "input_x" ] , [ inputOrtValue ] , [ "output" ] ) ;
Assert . Single ( outputs ) ;
var output = outputs [ 0 ] . GetTensorDataAsSpan < float > ( ) ;
Assert . Equal ( expectedOutput . Length , output . Length ) ;
Assert . Equal ( expectedOutput , output . ToArray ( ) , new FloatComparer ( ) ) ;
}
[Fact(DisplayName = "TestInferenceWithLoraAdapterFromFile")]
private void TestInferenceWithLoraAdapterFromFile ( )
{
using var ortAdapter = CreateLoraAdapterFromFile ( ) ;
TestInferenceWithLoraAdapter ( ortAdapter ) ;
}
[Fact(DisplayName = "TestInferenceWithLoraAdapterFromArray")]
private void TestInferenceWithLoraAdapterFromArray ( )
{
using var ortAdapter = CreateLoraAdapterFromArray ( ) ;
TestInferenceWithLoraAdapter ( ortAdapter ) ;
}
2023-05-18 00:15:19 +00:00
// TestGpu() will test
// - the CUDA EP on CUDA enabled builds
// - the DML EP on DML enabled builds
// - the ROCm EP on ROCm enabled builds
2021-10-27 10:07:07 +00:00
[GpuFact(DisplayName = "TestGpu")]
private void TestGpu ( )
{
var tuple = OpenSessionSqueezeNet ( 0 ) ; // run on deviceID 0
float [ ] expectedOutput = TestDataLoader . LoadTensorFromFile ( @"bench.expected_out" ) ;
using ( var session = tuple . Item1 )
{
var inputData = tuple . Item2 ;
var tensor = tuple . Item3 ;
var inputMeta = session . InputMetadata ;
var container = new List < NamedOnnxValue > ( ) ;
container . Add ( NamedOnnxValue . CreateFromTensor < float > ( "data_0" , tensor ) ) ;
var res = session . Run ( container ) ;
var resultArray = res . First ( ) . AsTensor < float > ( ) . ToArray ( ) ;
Assert . Equal ( expectedOutput , resultArray , new FloatComparer ( ) ) ;
}
}
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary ( string lpFileName ) ;
[DllImport("kernel32", CharSet = CharSet.Ansi)]
static extern UIntPtr GetProcAddress ( IntPtr hModule , string procName ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern bool FreeLibrary ( IntPtr hModule ) ;
[Fact(DisplayName = "VerifyNativeMethodsExist")]
private void VerifyNativeMethodsExist ( )
{
// Check for external API changes
if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
return ;
var entryPointNames = new [ ] {
"OrtGetApiBase" ,
"OrtSessionOptionsAppendExecutionProvider_CPU"
#if USE_DNNL
, "OrtSessionOptionsAppendExecutionProvider_Dnnl"
#endif
#if USE_CUDA
, "OrtSessionOptionsAppendExecutionProvider_CUDA"
#endif
#if USE_ROCM
, "OrtSessionOptionsAppendExecutionProvider_ROCM"
#endif
#if USE_DML
, "OrtSessionOptionsAppendExecutionProvider_DML"
#endif
#if USE_OPENVINO
, "OrtSessionOptionsAppendExecutionProvider_OpenVINO"
#endif
#if USE_TENSORRT
, "OrtSessionOptionsAppendExecutionProvider_Tensorrt"
#endif
#if USE_MIGRAPHX
, "OrtSessionOptionsAppendExecutionProvider_MIGraphX"
#endif
#if USE_NNAPI
, "OrtSessionOptionsAppendExecutionProvider_Nnapi"
#endif
} ;
IntPtr libraryHandle = IntPtr . Zero ;
try
{
libraryHandle = LoadLibrary ( module ) ;
foreach ( var ep in entryPointNames )
{
var x = GetProcAddress ( libraryHandle , ep ) ;
Assert . False ( x = = UIntPtr . Zero , $"Entrypoint {ep} not found in module {module}" ) ;
}
}
finally
{
UnloadLibrary ( libraryHandle ) ;
}
}
2025-01-27 18:58:38 +00:00
#if NET8_0_OR_GREATER
#pragma warning disable SYSLIB5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
private void ValidateRunResultData ( SystemNumericsTensors . Tensor < float > resultTensor , float [ ] expectedOutput , int [ ] expectedDimensions )
{
Assert . Equal ( expectedDimensions . Length , resultTensor . Rank ) ;
var resultDimensions = resultTensor . Lengths ;
for ( int i = 0 ; i < expectedDimensions . Length ; i + + )
{
Assert . Equal ( expectedDimensions [ i ] , resultDimensions [ i ] ) ;
}
var resultArray = resultTensor . ToArray ( ) ;
Assert . Equal ( expectedOutput . Length , resultArray . Length ) ;
Assert . Equal ( expectedOutput , resultArray , new FloatComparer ( ) ) ;
}
#pragma warning restore SYSLIB5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#endif
2021-10-27 10:07:07 +00:00
static string GetTestModelsDir ( )
{
// get build directory, append downloaded models location
var cwd = Directory . GetCurrentDirectory ( ) ;
var props = File . ReadAllLines ( Path . Combine ( cwd , propertiesFile ) ) ;
var modelsRelDir = Path . Combine ( props [ 0 ] . Split ( '=' ) [ 1 ] . Trim ( ) ) ;
var modelsDir = Path . Combine ( cwd , @"../../.." , modelsRelDir , "models" ) ;
return modelsDir ;
}
}
2022-06-28 01:49:19 +00:00
}