diff --git a/cmake/onnxruntime_csharp.cmake b/cmake/onnxruntime_csharp.cmake index 58a9f894ec..c65acd849f 100644 --- a/cmake/onnxruntime_csharp.cmake +++ b/cmake/onnxruntime_csharp.cmake @@ -50,6 +50,10 @@ if (onnxruntime_USE_XNNPACK) STRING(APPEND CSHARP_PREPROCESSOR_DEFINES "USE_XNNPACK;") endif() +if (onnxruntime_ENABLE_TRAINING_ON_DEVICE) + STRING(APPEND CSHARP_PREPROCESSOR_DEFINES "__TRAINING_ENABLED_NATIVE_BUILD__;") +endif() + include(CSharpUtilities) # generate Directory.Build.props diff --git a/csharp/OnnxRuntime.CSharp.proj b/csharp/OnnxRuntime.CSharp.proj index 94635f2dc3..5473246e8b 100644 --- a/csharp/OnnxRuntime.CSharp.proj +++ b/csharp/OnnxRuntime.CSharp.proj @@ -1,12 +1,12 @@ - + Microsoft.ML.OnnxRuntime Debug @@ -54,7 +54,7 @@ CMake creates a target to this project - + @@ -72,7 +72,7 @@ CMake creates a target to this project - + $([System.DateTime]::UtcNow.ToString(yyyyMMdd)) $([System.DateTime]::UtcNow.ToString(hhmm)) @@ -82,12 +82,12 @@ CMake creates a target to this project - + + Properties="NoBuild=true;Platform=AnyCPU;PackageVersion=$(PackageVersion);OrtPackageId=$(OrtPackageId);SelectedTargets=All"/> @@ -105,7 +105,7 @@ CMake creates a target to this project @@ -113,7 +113,7 @@ CMake creates a target to this project - + diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj b/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj index 679361c336..e6a8272fd8 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj +++ b/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj @@ -4,19 +4,19 @@ Microsoft.ML.OnnxRuntime - @@ -24,18 +24,18 @@ netstandard1.1;netstandard2.0;net5.0;netcoreapp3.1 - - xamarinios10;monoandroid11.0 - net6.0;net6.0-android;net6.0-ios;net6.0-macos @@ -64,7 +64,7 @@ $(ProjectDir)..\..\.. $(OnnxRuntimeRoot)\csharp - x64 + x64 Microsoft.ML.OnnxRuntime Microsoft.ML.OnnxRuntime @@ -72,11 +72,15 @@ false portable - - false + + true - + Microsoft.ML.OnnxRuntime.Managed Microsoft 1.0.0 @@ -105,27 +109,35 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb Debug;Release;RelWithDebInfo - true - true + true + true true - - true - true - true true - + + $(OrtConstants);__ENABLE_TRAINING_ON_DEVICE__ + + + @@ -146,7 +158,7 @@ $(OnnxRuntimeBuildDirectory)\$(Configuration) - @@ -179,14 +191,6 @@ $(OrtConstants);__XAMARIN__ - - - $(OrtConstants);__ENABLE_TRAINING_ON_DEVICE__ - - $(DefineConstants);$(OrtConstants) @@ -287,7 +291,7 @@ @@ -346,5 +350,5 @@ DestinationFolder="$(NativeBuildOutputDir)" /> - + diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/Training/CheckpointState.shared.cs b/csharp/src/Microsoft.ML.OnnxRuntime/Training/CheckpointState.shared.cs index 4cff5bccb1..fec2c18572 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/Training/CheckpointState.shared.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/Training/CheckpointState.shared.cs @@ -33,7 +33,7 @@ namespace Microsoft.ML.OnnxRuntime } else { - throw new InvalidOperationException("Training is disabled in the current build"); + throw new InvalidOperationException("Training is disabled in the current build. Please build ONNXRuntime from source with the build flags enable_training and enable_training_on_device. \n"); } } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/Training/TrainingSession.shared.cs b/csharp/src/Microsoft.ML.OnnxRuntime/Training/TrainingSession.shared.cs index b5b5a7c598..8d804628af 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/Training/TrainingSession.shared.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/Training/TrainingSession.shared.cs @@ -336,7 +336,7 @@ namespace Microsoft.ML.OnnxRuntime { if (!NativeTrainingMethods.TrainingEnabled()) { - throw new InvalidOperationException("Training is disabled in the current build."); + throw new InvalidOperationException("Training is disabled in the current build. Please build ONNXRuntime from source with the build flags enable_training and enable_training_on_device. \n"); } var options = sessOptions; if (sessOptions == null) diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TrainingTest.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TrainingTest.cs index f5bf62b4b8..f643efa720 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TrainingTest.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TrainingTest.cs @@ -23,6 +23,17 @@ namespace Microsoft.ML.OnnxRuntime.Tests this.output = o; } +#if !__TRAINING_ENABLED_NATIVE_BUILD__ + [Fact(DisplayName = "TestLoadCheckpointThrows")] + public void TestLoadCheckpointThrows() + { + string path = Path.Combine(Directory.GetCurrentDirectory(), "checkpoint.ckpt"); + var ex = Assert.Throws(() => { var opt = new CheckpointState(path); }); + Assert.Contains("Training is disabled in the current build.", ex.Message); + } +#endif + +#if __TRAINING_ENABLED_NATIVE_BUILD__ [Fact(DisplayName = "TestLoadCheckpoint")] public void TestLoadCheckpoint() { @@ -122,13 +133,13 @@ namespace Microsoft.ML.OnnxRuntime.Tests outputs = trainingSession.TrainStep(pinnedInputs); var outputBuffer = outputs.ElementAtOrDefault(0); - Assert.Equal("542.loss", outputBuffer.Name); + Assert.Equal("onnx::loss::21273", outputBuffer.Name); Assert.Equal(OnnxValueType.ONNX_TYPE_TENSOR, outputBuffer.ValueType); Assert.Equal(TensorElementType.Float, outputBuffer.ElementType); var outLabelTensor = outputBuffer.AsTensor(); Assert.NotNull(outLabelTensor); - Assert.Equal(expectedOutput, outLabelTensor, new FloatComparer()); + Assert.Equal(expectedOutput, outLabelTensor.ToArray(), new FloatComparer()); } } @@ -304,5 +315,6 @@ namespace Microsoft.ML.OnnxRuntime.Tests return x.GetHashCode(); } } - } +#endif + } } 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 fbb0417215..dafebd9ccb 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 @@ -17,10 +17,18 @@ ..\..\OnnxRuntime.snk Debug;Release;RelWithDebInfo - __NET_CORE_APP__;$(DefineConstants) - - false + + false + + + + $(ExtraDefineConstants);__TRAINING_ENABLED_NATIVE_BUILD__ + + + + __NET_CORE_APP__;$(DefineConstants);$(ExtraDefineConstants) @@ -95,12 +103,12 @@ ArrayTensorExtensionsTests.cs - - - TrainingTest.cs + + + PreserveNewest false diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index 6d094c8c6a..a6e6b4cdc7 100644 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -2131,7 +2131,17 @@ def derive_linux_build_property(): def build_nuget_package( - source_dir, build_dir, configs, use_cuda, use_openvino, use_tensorrt, use_dnnl, use_tvm, use_winml, use_snpe + source_dir, + build_dir, + configs, + use_cuda, + use_openvino, + use_tensorrt, + use_dnnl, + use_tvm, + use_winml, + use_snpe, + enable_training_on_device, ): if not (is_windows() or is_linux()): raise BuildError( @@ -2150,7 +2160,12 @@ def build_nuget_package( target_name = "/t:CreatePackage" execution_provider = '/p:ExecutionProvider="None"' package_name = '/p:OrtPackageId="Microsoft.ML.OnnxRuntime"' - if use_winml: + if enable_training_on_device: + if use_cuda: + package_name = '/p:OrtPackageId="Microsoft.ML.OnnxRuntime.Training.Gpu"' + else: + package_name = '/p:OrtPackageId="Microsoft.ML.OnnxRuntime.Training"' + elif use_winml: package_name = '/p:OrtPackageId="Microsoft.AI.MachineLearning"' target_name = "/t:CreateWindowsAIPackage" elif use_openvino: @@ -2210,7 +2225,7 @@ def build_nuget_package( run_subprocess(cmd_args, cwd=csharp_build_dir) if is_windows(): - if use_openvino or use_tvm: + if not use_winml: # user needs to make sure nuget is installed and added to the path variable nuget_exe = "nuget.exe" else: @@ -2237,12 +2252,11 @@ def build_nuget_package( run_subprocess(cmd_args, cwd=csharp_build_dir) -def run_csharp_tests(source_dir, build_dir, use_cuda, use_openvino, use_tensorrt, use_dnnl): +def run_csharp_tests(source_dir, build_dir, use_cuda, use_openvino, use_tensorrt, use_dnnl, enable_training_on_device): # Currently only running tests on windows. if not is_windows(): return csharp_source_dir = os.path.join(source_dir, "csharp") - is_linux_build = derive_linux_build_property() # define macros based on build args macros = "" @@ -2254,6 +2268,8 @@ def run_csharp_tests(source_dir, build_dir, use_cuda, use_openvino, use_tensorrt macros += "USE_DNNL;" if use_cuda: macros += "USE_CUDA;" + if enable_training_on_device: + macros += "__TRAINING_ENABLED_NATIVE_BUILD__;" define_constants = "" if macros != "": @@ -2268,10 +2284,9 @@ def run_csharp_tests(source_dir, build_dir, use_cuda, use_openvino, use_tensorrt cmd_args = [ "dotnet", "test", - "test\\Microsoft.ML.OnnxRuntime.Tests\\Microsoft.ML.OnnxRuntime.Tests.csproj", + "test\\Microsoft.ML.OnnxRuntime.Tests.NetCoreApp\\Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj", "--filter", "FullyQualifiedName!=Microsoft.ML.OnnxRuntime.Tests.InferenceTest.TestPreTrainedModels", - is_linux_build, define_constants, ort_build_dir, ] @@ -2811,10 +2826,19 @@ def main(): args.use_tvm, args.use_winml, args.use_snpe, + args.enable_training_on_device, ) if args.test and args.build_nuget: - run_csharp_tests(source_dir, build_dir, args.use_cuda, args.use_openvino, args.use_tensorrt, args.use_dnnl) + run_csharp_tests( + source_dir, + build_dir, + args.use_cuda, + args.use_openvino, + args.use_tensorrt, + args.use_dnnl, + args.enable_training_on_device, + ) if args.gen_doc: # special case CI where we create the build config separately to building diff --git a/tools/ci_build/github/azure-pipelines/templates/win-ci-vs-2019.yml b/tools/ci_build/github/azure-pipelines/templates/win-ci-vs-2019.yml index 932951c9f7..72d196bb38 100644 --- a/tools/ci_build/github/azure-pipelines/templates/win-ci-vs-2019.yml +++ b/tools/ci_build/github/azure-pipelines/templates/win-ci-vs-2019.yml @@ -192,7 +192,7 @@ jobs: solution: '$(Build.SourcesDirectory)\csharp\OnnxRuntime.CSharp.sln' configuration: '${{ parameters.BuildConfig }}' platform: 'Any CPU' - msbuildArguments: '-p:OnnxRuntimeBuildDirectory="$(Build.BinariesDirectory)" -p:OrtPackageId=$(OrtPackageId) -p:DefineConstants=USE_${{ parameters.ORT_EP_NAME }}' + msbuildArguments: '-p:OnnxRuntimeBuildDirectory="$(Build.BinariesDirectory)" -p:OrtPackageId=$(OrtPackageId)' workingDirectory: '$(Build.SourcesDirectory)\csharp' # C# test isn't launched by build.py, so models link has to be added. @@ -208,7 +208,7 @@ jobs: command: test projects: '$(Build.SourcesDirectory)\csharp\test\Microsoft.ML.OnnxRuntime.Tests.NetCoreApp\Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj' configuration: '${{ parameters.BuildConfig }}' - arguments: '--configuration ${{ parameters.BuildConfig }} -p:Platform="Any CPU" -p:OnnxRuntimeBuildDirectory="$(Build.BinariesDirectory)" -p:OrtPackageId=$(OrtPackageId) -p:DefineConstants=USE_${{ parameters.ORT_EP_NAME }} --blame' + arguments: '--configuration ${{ parameters.BuildConfig }} -p:Platform="Any CPU" -p:OnnxRuntimeBuildDirectory="$(Build.BinariesDirectory)" -p:OrtPackageId=$(OrtPackageId) --blame' workingDirectory: '$(Build.SourcesDirectory)\csharp' - ${{ if eq(parameters.EnablePython, true) }}: diff --git a/tools/nuget/generate_nuspec_for_native_nuget.py b/tools/nuget/generate_nuspec_for_native_nuget.py index c49cfa5723..532ed8d1ef 100644 --- a/tools/nuget/generate_nuspec_for_native_nuget.py +++ b/tools/nuget/generate_nuspec_for_native_nuget.py @@ -306,6 +306,10 @@ def generate_files(line_list, args): is_dml_package = args.package_name == "Microsoft.ML.OnnxRuntime.DirectML" is_windowsai_package = args.package_name == "Microsoft.AI.MachineLearning" is_snpe_package = args.package_name == "Microsoft.ML.OnnxRuntime.Snpe" + is_training_package = args.package_name in [ + "Microsoft.ML.OnnxRuntime.Training", + "Microsoft.ML.OnnxRuntime.Training.Gpu", + ] includes_winml = is_windowsai_package includes_directml = (is_dml_package or is_windowsai_package) and ( @@ -380,6 +384,16 @@ def generate_files(line_list, args): + '" target="build\\native\\include" />' ) + if is_training_package: + files_list.append( + "' + ) + if args.execution_provider == "tvm": files_list.append( "