From 34d970f780e1fdf6f8668abbe505d72bcf320d5e Mon Sep 17 00:00:00 2001 From: harshithapv <54084812+harshithapv@users.noreply.github.com> Date: Fri, 4 Oct 2019 10:03:36 -0700 Subject: [PATCH 01/28] Buffer re-use using ArrayPool and a few more checks (#4293) * commit b468adbd7d3b625154bc991cb6f0931198303e25 Author: Harshitha Parnandi Venkata Date: Tue Oct 1 21:19:57 2019 -0700 Fixed a bug in the unit test for image classification commit 30aa4d1d7790a2986bb0986b86cc1cea6b954b4d Author: Harshitha Parnandi Venkata Date: Tue Oct 1 20:43:17 2019 -0700 addressed Zeeshan's comments commit 3d4f5fe616380dc5190701edc170185f39ab82a1 Merge: 0fbd3d28 718a238f Author: Harshitha Parnandi Venkata Date: Tue Oct 1 20:41:21 2019 -0700 Merge branch 'master' of https://github.com/dotnet/machinelearning into ImageClassificationVBuf commit 0fbd3d28758d2fefd7b970a94380f89d0032a355 Author: Harshitha Parnandi Venkata Date: Tue Oct 1 17:10:49 2019 -0700 Changed type to useImageType in LoadImages(). Changed appropriate variable names in ImageClassificationTransform.cs commit 241788892b1f623870d33db1190ac7fdc716ce02 Merge: 3ad26b4a 4944be7e Author: Harshitha Parnandi Venkata Date: Tue Oct 1 16:55:25 2019 -0700 Merge branch 'master' of https://github.com/dotnet/machinelearning into ImageClassificationVBuf commit 3ad26b4a1a062f8fd29cdc524df0bf771f6cbdde Author: Harshitha Parnandi Venkata Date: Tue Oct 1 15:59:06 2019 -0700 Added buffer re-use while reading the image in netstandard 2.0. Addressed Eric's comments. Changed ImageLoadingTransformer to take a bool type instead of a DataViewType to make it user friendly. (type = true means we are using VBuffer , type = false means we are using ImageDataViewType) commit c67dd08b6d5ff450060fda8538f30d51faad936c Author: Harshitha Parnandi Venkata Date: Tue Oct 1 09:50:52 2019 -0700 Added functionality to load images as VBuffer in ImageLoader. If no DataViewType options are provided it defaults to loading images as ImageDataViewType. Made LoadImages a part of the sample in ResnetV2101TransferLearningTrainTestSplit.cs. Addressed some of the comments from Zeeshan and Yael. Added a unit test for testing the API. Added TargetFrameworks to get cross platform functionality for System.IO.Stream.Read(Span) which doesn't work for netstandard2.0. commit ae2ac0de64276252ae76ecfc55e74c82b9e77917 Author: Harshitha Parnandi Venkata Date: Wed Sep 25 14:49:41 2019 -0700 Added some edits to address Yael's comments commit b1e57396bc808b1ba0e5ee27702eedff318438f7 Author: Harshitha Parnandi Venkata Date: Wed Sep 25 13:24:03 2019 -0700 Added unit test for the change commit acf985d153766b7a8c2f8684a5ae5f81b6b0f67f Author: Harshitha Parnandi Venkata Date: Mon Sep 23 10:39:07 2019 -0700 Changed the calling function back to how it was in master commit b80f7add1b990f1ba67d465dcb53c90d018345d2 Author: Harshitha Parnandi Venkata Date: Mon Sep 23 10:20:31 2019 -0700 Added a few optimizations to re-use buffers and thereby improving performance. commit b106ae06a7867b8ba0be1643ff04e76e61445c7d Author: Harshitha Parnandi Venkata Date: Thu Sep 19 14:07:15 2019 -0700 Changed Image Classification API to take in a VBuffer type instead of ImagePath. * fixed merge conflicts * Fixed some unit tests that were failing after the merge. Addressed a few comments. * Fixed TensorFlow unit tests * Changed the buffer re-use logic for ReadToEnd * Changed ReadToEnd function to read using span instead of unsafe blocks * removed unnecessary commits * Added version check with backward compatability. Addressed Zeeshan's comments. * Fixed tab and synced to master * Addressed comments. Checkpoint commit * changed the solution files and version check in ImageLoader.cs * Added changes for StableApi.csproj * Added ArrayPool for buffer re-use * Handled the case when MakeGetter src is empty we need to send an empty VBuffer. Another check for handling empty images. * Addressed comments --- build/Dependencies.props | 1 + ...snetV2101TransferLearningTrainTestSplit.cs | 17 +++-- .../Microsoft.ML.ImageAnalytics.nupkgproj | 1 + .../ImageClassificationTransform.cs | 3 + .../ImageLoader.cs | 63 +++++++++++-------- .../Microsoft.ML.ImageAnalytics.csproj | 4 +- 6 files changed, 53 insertions(+), 36 deletions(-) diff --git a/build/Dependencies.props b/build/Dependencies.props index bd66c41aad..20b5310963 100644 --- a/build/Dependencies.props +++ b/build/Dependencies.props @@ -3,6 +3,7 @@ 10.0.3 + 4.4.0 4.4.0 1.5.0 4.5.1 diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs index 3bb6238cbd..f8627ff372 100644 --- a/docs/samples/Microsoft.ML.Samples/Dynamic/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs +++ b/docs/samples/Microsoft.ML.Samples/Dynamic/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs @@ -2,15 +2,15 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Threading; using System.Threading.Tasks; using Microsoft.ML; +using Microsoft.ML.Data; using Microsoft.ML.Transforms; using static Microsoft.ML.DataOperationsCatalog; -using System.Linq; -using Microsoft.ML.Data; -using System.IO.Compression; -using System.Threading; -using System.Net; namespace Samples.Dynamic { @@ -131,9 +131,6 @@ private static void TrySinglePrediction(string imagesForPredictions, IEnumerable testImages = LoadImagesFromDirectory( imagesForPredictions, false); - byte[] imgBytes = File.ReadAllBytes(testImages.First().ImagePath); - VBuffer imgData = new VBuffer(imgBytes.Length, imgBytes); - ImageData imageToPredict = new ImageData { ImagePath = testImages.First().ImagePath @@ -169,7 +166,7 @@ private static void EvaluateModel(MLContext mlContext, Console.WriteLine("Predicting and Evaluation took: " + (elapsed2Ms / 1000).ToString() + " seconds"); } - + public static IEnumerable LoadImagesFromDirectory(string folder, bool useFolderNameAsLabel = true) { @@ -194,7 +191,7 @@ public static IEnumerable LoadImagesFromDirectory(string folder, } } } - + yield return new ImageData() { ImagePath = file, diff --git a/pkg/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.nupkgproj b/pkg/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.nupkgproj index 763256ff51..bb59e06653 100644 --- a/pkg/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.nupkgproj +++ b/pkg/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.nupkgproj @@ -7,6 +7,7 @@ + diff --git a/src/Microsoft.ML.Dnn/ImageClassificationTransform.cs b/src/Microsoft.ML.Dnn/ImageClassificationTransform.cs index fe0ab3d61f..d2eb4ccbed 100644 --- a/src/Microsoft.ML.Dnn/ImageClassificationTransform.cs +++ b/src/Microsoft.ML.Dnn/ImageClassificationTransform.cs @@ -275,6 +275,9 @@ private void CacheFeaturizedImagesToDisk(IDataView input, string labelColumnName { labelGetter(ref label); imageGetter(ref image); + if (image.Length <= 0) + continue; //Empty Image + var imageTensor = imageProcessor.ProcessImage(image); runner.AddInput(imageTensor, 0); var featurizedImage = runner.Run()[0]; // Reuse memory diff --git a/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs b/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs index 5be7476b2c..6a981b6b70 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; @@ -189,14 +190,12 @@ private sealed class Mapper : OneToOneMapperBase { private readonly ImageLoadingTransformer _parent; private readonly bool _type; - private readonly ConcurrentBag _bufferPool; public Mapper(ImageLoadingTransformer parent, DataViewSchema inputSchema, bool type) : base(parent.Host.Register(nameof(Mapper)), parent, inputSchema) { _type = type; _parent = parent; - _bufferPool = new ConcurrentBag(); } protected override Delegate MakeGetter(DataViewRow input, int iinfo, Func activeOutput, out Action disposer) @@ -255,31 +254,30 @@ private Delegate MakeGetterVectorDataViewByteType(DataViewRow input, int iinfo, ValueGetter> del = (ref VBuffer dst) => { - byte[] buffer = null; - if (!_bufferPool.TryTake(out buffer)) - { - buffer = new byte[4096]; - } - getSrc(ref src); - if (src.Length > 0) { string path = src.ToString(); if (!string.IsNullOrWhiteSpace(_parent.ImageFolder)) path = Path.Combine(_parent.ImageFolder, path); - if (!TryLoadDataIntoBuffer(path, ref dst, buffer)) - throw Host.Except($"Failed to load image {src.ToString()}."); + if (!TryLoadDataIntoBuffer(path, ref dst)) + { + var editor = VBufferEditor.Create(ref dst, 0); //Empty Image + dst = editor.Commit(); + } + } + else + { + var editor = VBufferEditor.Create(ref dst, 0 ); + dst = editor.Commit(); } - Contract.Assert(buffer != null); - _bufferPool.Add(buffer); }; return del; } - private static bool TryLoadDataIntoBuffer(string path, ref VBuffer imgData, byte[] readBuffer) + private static bool TryLoadDataIntoBuffer(string path, ref VBuffer imgData) { int count = -1; int bytesread = -1; @@ -302,28 +300,43 @@ private static bool TryLoadDataIntoBuffer(string path, ref VBuffer imgData count = (int)fileLength; var editor = VBufferEditor.Create(ref imgData, count); - bytesread = ReadToEnd(fs, editor.Values, readBuffer); + bytesread = ReadToEnd(fs, editor.Values); imgData = editor.Commit(); return (count > 0); } } - private static int ReadToEnd(System.IO.Stream stream, Span bufferSpan, byte[] readBuffer) + private static int ReadToEnd(System.IO.Stream stream, Span bufferSpan) { int totalBytesRead = 0; int bytesRead; - var readBufferSpan = readBuffer.AsSpan(); - var srcSpan = readBufferSpan; - while ((bytesRead = stream.Read(readBuffer, 0, readBuffer.Length)) > 0) + + int chunksize = 4096; // Most optimal size for buffer, friendly to CPU's L1 cache + var bufferPool = ArrayPool.Shared; + byte[] readBuffer = bufferPool.Rent(chunksize); + + try { - if (bytesRead != srcSpan.Length) - srcSpan = readBufferSpan.Slice(0, bytesRead); - var dstSpan = bufferSpan.Slice(totalBytesRead, bytesRead); - Contract.Assert(srcSpan.Length == dstSpan.Length); - srcSpan.CopyTo(dstSpan); - totalBytesRead += bytesRead; + var readBufferSpan = readBuffer.AsSpan(); + var srcSpan = readBufferSpan; + while ((bytesRead = stream.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + if (bytesRead != srcSpan.Length) + srcSpan = readBufferSpan.Slice(0, bytesRead); + var dstSpan = bufferSpan.Slice(totalBytesRead, bytesRead); + Contract.Assert(srcSpan.Length == dstSpan.Length); + srcSpan.CopyTo(dstSpan); + totalBytesRead += bytesRead; + } } + finally + { + + // don't use the reference to the buffer after returning it! + bufferPool.Return(readBuffer); + } + return totalBytesRead; } diff --git a/src/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.csproj b/src/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.csproj index 0bc4d94f1a..e32f0db25e 100644 --- a/src/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.csproj +++ b/src/Microsoft.ML.ImageAnalytics/Microsoft.ML.ImageAnalytics.csproj @@ -6,11 +6,13 @@ + - + + From 9bc3d7be8c5590b0a4858ae4bc2cc6b3ab0a5ea4 Mon Sep 17 00:00:00 2001 From: ashbhandare Date: Fri, 4 Oct 2019 16:16:36 -0700 Subject: [PATCH 02/28] Image Classification API: Fix processing incomplete batch( 0) + { + featureTensorShape[0] = batchIndex; + featureBatchSizeInBytes = sizeof(float) * featureLength * batchIndex; + labelTensorShape[0] = batchIndex; + labelBatchSizeInBytes = sizeof(long) * batchIndex; + runner.AddInput(new Tensor(featureBatchPtr, featureTensorShape, TF_DataType.TF_FLOAT, featureBatchSizeInBytes), 0) + .AddInput(new Tensor(labelBatchPtr, labelTensorShape, TF_DataType.TF_INT64, labelBatchSizeInBytes), 1) + .Run(); + + metrics.Train.BatchProcessedCount += 1; + + if (options.TestOnTrainSet && statisticsCallback != null) + { + var outputTensors = testEvalRunner + .AddInput(new Tensor(featureBatchPtr, featureTensorShape, TF_DataType.TF_FLOAT, featureBatchSizeInBytes), 0) + .AddInput(new Tensor(labelBatchPtr, labelTensorShape, TF_DataType.TF_INT64, labelBatchSizeInBytes), 1) + .Run(); + + outputTensors[0].ToScalar(ref accuracy); + outputTensors[1].ToScalar(ref crossentropy); + metrics.Train.Accuracy += accuracy; + metrics.Train.CrossEntropy += crossentropy; + + outputTensors[0].Dispose(); + outputTensors[1].Dispose(); + } + + batchIndex = 0; + featureTensorShape[0] = batchSize; + featureBatchSizeInBytes = sizeof(float) * featureBatch.Length; + labelTensorShape[0] = batchSize; + labelBatchSizeInBytes = sizeof(long) * batchSize; + } + if (options.TestOnTrainSet && statisticsCallback != null) { metrics.Train.Epoch = epoch; @@ -443,7 +483,15 @@ private void TrainAndEvaluateClassificationLayer(string trainBottleneckFilePath, } if (validationSet == null) + { + //Early stopping check + if (options.EarlyStoppingCriteria != null) + { + if (options.EarlyStoppingCriteria.ShouldStop(metrics.Train)) + break; + } continue; + } batchIndex = 0; metrics.Train.BatchProcessedCount = 0; @@ -481,6 +529,31 @@ private void TrainAndEvaluateClassificationLayer(string trainBottleneckFilePath, } } + //Process last incomplete batch + if(batchIndex > 0) + { + featureTensorShape[0] = batchIndex; + featureBatchSizeInBytes = sizeof(float) * featureLength * batchIndex; + labelTensorShape[0] = batchIndex; + labelBatchSizeInBytes = sizeof(long) * batchIndex; + var outputTensors = validationEvalRunner + .AddInput(new Tensor(featureBatchPtr, featureTensorShape, TF_DataType.TF_FLOAT, featureBatchSizeInBytes), 0) + .AddInput(new Tensor(labelBatchPtr, labelTensorShape, TF_DataType.TF_INT64, labelBatchSizeInBytes), 1) + .Run(); + + outputTensors[0].ToScalar(ref accuracy); + metrics.Train.Accuracy += accuracy; + metrics.Train.BatchProcessedCount += 1; + batchIndex = 0; + + featureTensorShape[0] = batchSize; + featureBatchSizeInBytes = sizeof(float) * featureBatch.Length; + labelTensorShape[0] = batchSize; + labelBatchSizeInBytes = sizeof(long) * batchSize; + + outputTensors[0].Dispose(); + } + if (statisticsCallback != null) { metrics.Train.Epoch = epoch; From c073e6b3bcb271afe002cd1fb354bb2309551a28 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Tue, 28 Apr 2020 12:25:38 -0700 Subject: [PATCH 03/28] upgrade to 3.1 --- .../Templates/Console/PredictProject.cs | 2 +- .../Templates/Console/PredictProject.tt | 2 +- ...s.AzureCodeGeneratorTest.test.ConsoleApp.csproj.approved.txt | 2 +- ...CodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt | 2 +- ...GeneratorTests.ConsoleAppProjectFileContentTest.approved.txt | 2 +- ...eConsoleAppProjectContents_VerifyPredictProject.approved.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs index 96c4bbf124..29f288a076 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs @@ -27,7 +27,7 @@ internal partial class PredictProject : PredictProjectBase public virtual string TransformText() { this.Write("\r\n\r\n \r\n Exe\r\n netcoreapp2.1\r\n \r\n netcoreapp3.1\r\n \r\n \r\n \r\n"); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt index 1dd932085e..15e82d409c 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt @@ -8,7 +8,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.ConsoleApp.csproj.approved.txt index 66e5ca3695..f94467d5dc 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.ConsoleApp.csproj.approved.txt @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index 23fa52e0c1..5a0f249e82 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProjectFileContentTest.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProjectFileContentTest.approved.txt index 9ef4077dee..97df9ea672 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProjectFileContentTest.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.ConsoleAppProjectFileContentTest.approved.txt @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProject.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProject.approved.txt index 14a45f8c0c..e261bed0fd 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProject.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.Recommendation_GenerateConsoleAppProjectContents_VerifyPredictProject.approved.txt @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 From 8f0fc1a59dd3aa6186ec4e94f9cccf93fab4dae4 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Tue, 28 Apr 2020 12:31:31 -0700 Subject: [PATCH 04/28] write inline data using invariantCulture --- src/Microsoft.ML.CodeGenerator/Utils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.ML.CodeGenerator/Utils.cs b/src/Microsoft.ML.CodeGenerator/Utils.cs index 216097064c..075bc577ce 100644 --- a/src/Microsoft.ML.CodeGenerator/Utils.cs +++ b/src/Microsoft.ML.CodeGenerator/Utils.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -101,7 +102,7 @@ internal static string GetValueFromColumn(DataViewRowCursor rowCursor, DataVi return "Single.NegativeInfinity"; } - return f?.ToString() + "F"; + return f?.ToString(CultureInfo.InvariantCulture) + "F"; } if (val is bool) From c8f154b50aa4d8cd864a210ad766b333cc63c33d Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 2 Jun 2020 14:37:58 -0700 Subject: [PATCH 05/28] Update: ModelBuilder codegen for Object Detection --- src/Microsoft.ML.AutoML/TaskKind.cs | 3 +- .../AzureAttachModelCodeGenerator.cs | 49 +++ .../CSharp/CodeGeneratorSettings.cs | 2 + .../CSharp/TransformGeneratorFactory.cs | 9 + .../CSharp/TransformGenerators.cs | 28 ++ .../Microsoft.ML.CodeGenerator.csproj | 38 +- .../Azure/Model/ObjectDetectionOnnxMapping.cs | 378 +++++++++++++++++ .../Azure/Model/ObjectDetectionOnnxMapping.tt | 118 ++++++ .../Azure/Model/ReshapeTransformer.cs | 381 ++++++++++++++++++ .../Azure/Model/ReshapeTransformer.tt | 62 +++ .../Templates/Console/ConsumeModel.cs | 12 + .../Templates/Console/ConsumeModel.tt | 10 + .../Templates/Console/ModelOutputClass.cs | 9 +- .../Templates/Console/ModelOutputClass.tt | 12 +- ...CodeGenTest.ConsoleApp.csproj.approved.txt | 18 + ...Test.CodeGenTest.Model.csproj.approved.txt | 23 ++ ...GeneratorTest.ConsumeModel.cs.approved.txt | 43 ++ ...GeneratorTest.LabelMapping.cs.approved.txt | 104 +++++ ...GeneratorTest.ModelBuilder.cs.approved.txt | 79 ++++ ...deGeneratorTest.ModelInput.cs.approved.txt | 18 + ...eGeneratorTest.ModelOutput.cs.approved.txt | 19 + ...ratorTest.NormalizeMapping.cs.approved.txt | 50 +++ ...nCodeGeneratorTest.Program.cs.approved.txt | 27 ++ ...torTest.ReshapeTransformer.cs.approved.txt | 48 +++ ...s.PredictionCSFileContentTest.approved.txt | 1 - .../ConsoleCodeGeneratorTests.cs | 95 +++++ 26 files changed, 1624 insertions(+), 12 deletions(-) create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelInput.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt diff --git a/src/Microsoft.ML.AutoML/TaskKind.cs b/src/Microsoft.ML.AutoML/TaskKind.cs index a469be37a6..a2000f3b95 100644 --- a/src/Microsoft.ML.AutoML/TaskKind.cs +++ b/src/Microsoft.ML.AutoML/TaskKind.cs @@ -9,6 +9,7 @@ internal enum TaskKind BinaryClassification, MulticlassClassification, Regression, - Recommendation + Recommendation, + ObjectDetection } } diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index 37b260fb72..71f7b9ebb0 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -31,6 +31,9 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator public ICSharpFile ConsumeModel { get; private set; } public ICSharpFile LabelMapping { get; private set; } public ICSharpFile ImageLabelMapping { get; private set; } + public ICSharpFile ObjectDetectionLabelMapping { get; private set; } + public ICSharpFile ReshapeTransformerMap { get; private set; } + public ICSharpFile ObjectDetectionConsumeModel { get; private set; } public string Name { get; set; } public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults columnInferenceResults, CodeGeneratorSettings options, string namespaceValue) @@ -119,6 +122,41 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c Name = "LabelMapping.cs", }; + // Tevin: added + ObjectDetectionLabelMapping = new CSharpCodeFile() + { + File = new ObjectDetectionOnnxMapping() + { + Target = _settings.Target, + Namespace = _nameSpaceValue, + ImportLabels = _settings.ClassificationLabel, + }.TransformText(), + Name = "LabelMapping.cs", + }; + + ReshapeTransformerMap = new CSharpCodeFile() + { + File = new ReshapeTransformer() + { + Target = _settings.Target, + Namespace = _nameSpaceValue, + }.TransformText(), + Name = "ReshapeTransformer.cs", + }; + + ObjectDetectionConsumeModel = new CSharpCodeFile() + { + File = new ConsumeModel() + { + Namespace = _nameSpaceValue, + Target = _settings.Target, + HasObjectDetectionLabelMapping = _settings.IsObjectDetection, + HasReshapeTransformer = _settings.IsObjectDetection, + MLNetModelpath = _settings.ModelPath, + }.TransformText(), + Name = "ConsumeModel.cs" + }; + ConsumeModel = new CSharpCodeFile() { File = new ConsumeModel() @@ -147,6 +185,17 @@ public ICSharpProject ToProject() NormalizeMapping, ImageLabelMapping, }; + } else if(_settings.IsObjectDetection) + { + project = new CSharpProject() + { + ModelInputClass, + ModelOutputClass, + ObjectDetectionConsumeModel, + ModelProject, + ReshapeTransformerMap, + ObjectDetectionLabelMapping, + }; } else { diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs index 1fd4840055..164452eb2b 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs @@ -41,6 +41,8 @@ public CodeGeneratorSettings() public bool IsImage { get; set; } + public bool IsObjectDetection { get; set; } + /// /// classification label /// for Azure image only diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs index 7075233309..e9ff0b90e7 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs @@ -22,6 +22,9 @@ internal enum SpecialTransformer ExtractPixel = 2, NormalizeMapping = 3, LabelMapping = 4, + ObjectDetectionLabelMapping = 5, // Tevin: added for OD mappings + ReshapeTransformer = 6, + } internal static class TransformGeneratorFactory { @@ -97,6 +100,12 @@ internal static ITransformGenerator GetInstance(PipelineNode node) case SpecialTransformer.LabelMapping: result = new CustomLabelMapping(node); break; + case SpecialTransformer.ObjectDetectionLabelMapping: + result = new CustomObjectDetectionLabelMapping(node); + break; + case SpecialTransformer.ReshapeTransformer: + result = new CustomReshapeTransformer(node); + break; default: return null; } diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index aa44f1f349..5a8c10323e 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -414,6 +414,34 @@ public override string GenerateTransformer() } } + // Tevin: added to include OD label mapping + internal class CustomObjectDetectionLabelMapping: TransformGeneratorBase + { + public CustomObjectDetectionLabelMapping(PipelineNode node) : base(node) { } + internal override string MethodName => "ObjectDetectionLabelMapping"; + + public override string GenerateTransformer() + { + return @"CustomMapping( // added this line not sure what to do. + (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), + contractName: nameof(ObjectDetectionLabelMapping))"; + } + } + + // Tevin: added to have the reshaper for OD + internal class CustomReshapeTransformer : TransformGeneratorBase + { + public CustomReshapeTransformer(PipelineNode node) : base(node) { } + internal override string MethodName => "ObjectDetectionLabelMapping"; + + public override string GenerateTransformer() + { + return @"CustomMapping( + (input, output) => ReshapeTransformer.Mapping(input, output), + contractName: nameof(ReshapeTransformer))"; + } + } + internal class ApplyOnnxModel : TransformGeneratorBase { public ApplyOnnxModel(PipelineNode node) : base(node) { } diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index 332b86e217..995766426b 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -11,6 +11,14 @@ + + + True + True + ReshapeTransformer.tt + + + @@ -33,10 +41,6 @@ TextTemplatingFilePreprocessor ImageLabelMapping.cs - - TextTemplatingFilePreprocessor - OnnxModelOutputClass.cs - TextTemplatingFilePreprocessor NormalizeMapping.cs @@ -45,6 +49,18 @@ TextTemplatingFilePreprocessor LabelMapping.cs + + TextTemplatingFilePreprocessor + ObjectDetectionOnnxMapping.cs + + + TextTemplatingFilePreprocessor + OnnxModelOutputClass.cs + + + TextTemplatingFilePreprocessor + ReshapeTransformer.cs + @@ -97,10 +113,10 @@ True ImageLabelMapping.tt - + True True - OnnxModelOutputClass.tt + ObjectDetectionOnnxMapping.tt True @@ -112,6 +128,16 @@ True LabelMapping.tt + + True + True + OnnxModelOutputClass.tt + + + True + True + ReshapeTransformer.tt + True True diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs new file mode 100644 index 0000000000..5081008baf --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs @@ -0,0 +1,378 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 16.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using System; + + /// + /// Class to produce the template output + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal partial class ObjectDetectionOnnxMapping : ObjectDetectionOnnxMappingBase + { + /// + /// Create the template output + /// + public virtual string TransformText() + { +if(Target == CSharp.GenerateTarget.Cli){ +CLI_Annotation(); + } else if(Target == CSharp.GenerateTarget.ModelBuilder){ +MB_Annotation(); + } + this.Write("\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML.Transforms;\r\nusing System;\r\nusing " + + "System.Linq;\r\n\r\nnamespace "); + this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); + this.Write(@".Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[]{"); +foreach(var label in ImportLabels){ + this.Write("\""); + this.Write(this.ToStringHelper.ToStringWithCulture(label)); + this.Write("\","); +} + this.Write("};\r\n\r\n public static void Mapping(ObjectDetectionLabelMappingInput input, " + + "ModelOutput output)\r\n {\r\n long[] oldLabels = input.Labels.GetV" + + "alues().ToArray();\r\n output.Labels = new string[oldLabels.Length];\r\n " + + " output.Scores = input.Scores.GetValues().ToArray();\r\n outp" + + "ut.Boxes = input.Boxes.GetValues().ToArray();\r\n for (int i = 0; i < o" + + "ldLabels.Length; i++)\r\n {\r\n output.Labels[i] = Label[o" + + "ldLabels[i]];\r\n }\r\n }\r\n\r\n // This factory method will b" + + "e called when loading the model to get the mapping operation.\r\n public ov" + + "erride Action GetMapping()\r\n " + + " {\r\n return Mapping;\r\n }\r\n }\r\n\r\n public class ObjectDe" + + "tectionLabelMappingInput\r\n {\r\n [ColumnName(\"labels\")]\r\n public " + + "VBuffer Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public VBuffer<" + + "float> Scores;\r\n\r\n [ColumnName(\"boxes\")]\r\n public VBuffer B" + + "oxes;\r\n }\r\n\r\n public class BoundingBox\r\n {\r\n public float Top;\r\n" + + "\r\n public float Left;\r\n\r\n public float Right;\r\n\r\n public fl" + + "oat Bottom;\r\n\r\n public string Label;\r\n\r\n public float Score;\r\n\r\n " + + " public override string ToString()\r\n {\r\n return $\"Top: {t" + + "his.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: " + + "{this.Label}, Score: {this.Score}\";\r\n }\r\n }\r\n\r\n\r\n public class Mode" + + "lOutput\r\n {\r\n [ColumnName(\"boxes\")]\r\n public float[] Boxes;\r\n\r\n" + + " [ColumnName(\"PredictedLabels\")]\r\n public string[] Labels;\r\n\r\n " + + " [ColumnName(\"scores\")]\r\n public float[] Scores;\r\n\r\n private Bo" + + "undingBox[] BoundingBoxes\r\n {\r\n get\r\n {\r\n " + + " var boundingBoxes = new List();\r\n return bound" + + "ingBoxes.ToArray();\r\n }\r\n }\r\n\r\n public BoundingBox[] Ge" + + "tBoundingBoxes()\r\n {\r\n return this.BoundingBoxes;\r\n }\r\n" + + "\r\n public override string ToString()\r\n {\r\n var sb = new" + + " StringBuilder();\r\n\r\n foreach (var box in this.BoundingBoxes)\r\n " + + " {\r\n sb.AppendLine(box.ToString());\r\n }\r\n\r\n " + + " return sb.ToString();\r\n }\r\n }\r\n}\r\n"); + return this.GenerationEnvironment.ToString(); + } + +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} +public string[] ImportLabels {get; set;} = new string[0]; + + +void CLI_Annotation() +{ +this.Write(@"//***************************************************************************************** +//* * +//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * +//* * +//***************************************************************************************** +"); + + +} + + +void MB_Annotation() +{ +this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); + + +} + + } + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal class ObjectDetectionOnnxMappingBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt new file mode 100644 index 0000000000..b521f8c0e3 --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt @@ -0,0 +1,118 @@ +<#@ template language="C#" linePragmas="false" visibility="internal" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> +<#CLI_Annotation();#> +<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> +<#MB_Annotation();#> +<# } #> + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace <#= Namespace #>.Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[]{<#foreach(var label in ImportLabels){#>"<#=label#>",<#}#>}; + + public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + { + long[] oldLabels = input.Labels.GetValues().ToArray(); + output.Labels = new string[oldLabels.Length]; + output.Scores = input.Scores.GetValues().ToArray(); + output.Boxes = input.Boxes.GetValues().ToArray(); + for (int i = 0; i < oldLabels.Length; i++) + { + output.Labels[i] = Label[oldLabels[i]]; + } + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class ObjectDetectionLabelMappingInput + { + [ColumnName("labels")] + public VBuffer Labels; + + [ColumnName("scores")] + public VBuffer Scores; + + [ColumnName("boxes")] + public VBuffer Boxes; + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } + + + public class ModelOutput + { + [ColumnName("boxes")] + public float[] Boxes; + + [ColumnName("PredictedLabels")] + public string[] Labels; + + [ColumnName("scores")] + public float[] Scores; + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + return boundingBoxes.ToArray(); + } + } + + public BoundingBox[] GetBoundingBoxes() + { + return this.BoundingBoxes; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var box in this.BoundingBoxes) + { + sb.AppendLine(box.ToString()); + } + + return sb.ToString(); + } + } +} +<#+ +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} +public string[] ImportLabels {get; set;} = new string[0]; +#> \ No newline at end of file diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs new file mode 100644 index 0000000000..9c214842b0 --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs @@ -0,0 +1,381 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 16.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using System; + + /// + /// Class to produce the template output + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal partial class ReshapeTransformer : ReshapeTransformerBase + { + /// + /// Create the template output + /// + public virtual string TransformText() + { +if(Target == CSharp.GenerateTarget.Cli){ +CLI_Annotation(); + } else if(Target == CSharp.GenerateTarget.ModelBuilder){ +MB_Annotation(); + } + this.Write(@" +using System; +using System.Linq; +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; + +namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers +{ + [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] + class ReshapeTransformer : CustomMappingFactory + { + // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. + public static void Mapping(Input input, Output output) + { + var values = input.Reshape.GetValues().ToArray(); + + for (int x = 0; x < values.Count(); x++) + { + // normalize from [0, 255] to [0, 1] + values[x] /= 255; + }; + + output.Reshape = new VBuffer(values.Count(), values); + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class Input + { + [ColumnName(""input"")] + [VectorType(3, 800, 600)] + public VBuffer Reshape; + } + + public class Output + { + [ColumnName(""input"")] + [VectorType(1 * 3 * 600 * 800)] + public VBuffer Reshape; + } +} + +"); + return this.GenerationEnvironment.ToString(); + } + +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} + + +void CLI_Annotation() +{ +this.Write(@"//***************************************************************************************** +//* * +//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * +//* * +//***************************************************************************************** +"); + + +} + + +void MB_Annotation() +{ +this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); + + +} + + } + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal class ReshapeTransformerBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt new file mode 100644 index 0000000000..270496ba71 --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt @@ -0,0 +1,62 @@ +<#@ template language="C#" linePragmas="false" visibility="internal" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> +<#CLI_Annotation();#> +<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> +<#MB_Annotation();#> +<# } #> + +using System; +using System.Linq; +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; + +namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers +{ + [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] + class ReshapeTransformer : CustomMappingFactory + { + // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. + public static void Mapping(Input input, Output output) + { + var values = input.Reshape.GetValues().ToArray(); + + for (int x = 0; x < values.Count(); x++) + { + // normalize from [0, 255] to [0, 1] + values[x] /= 255; + }; + + output.Reshape = new VBuffer(values.Count(), values); + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class Input + { + [ColumnName("input")] + [VectorType(3, 800, 600)] + public VBuffer Reshape; + } + + public class Output + { + [ColumnName("input")] + [VectorType(1 * 3 * 600 * 800)] + public VBuffer Reshape; + } +} + +<#+ +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} +#> + diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs index ba1983786b..79b4f8eec1 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs @@ -63,6 +63,16 @@ public static PredictionEngine CreatePredictionEngine() this.Write(" \r\n\t\t\t// Register LabelMapping to map predicted Labels to their corresponding pro" + "babilities (likelihood of specified Labels)\r\n mlContext.ComponentCata" + "log.RegisterAssembly(typeof(LabelMapping).Assembly);\r\n"); +} +if(HasReshapeTransformer){ + this.Write(" \r\n\t\t\t// Register ReshapeTransformer to calculate probabilities for each Label.\r\n" + + " mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransforme" + + "r).Assembly);\r\n"); +} +if(HasObjectDetectionLabelMapping){ + this.Write(" \r\n\t\t\t// Register ObjectDetectionMapping to map predicted labels to correct strin" + + "g.\r\n mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetecti" + + "onOnnxMapping).Assembly);\r\n"); } this.Write("\r\n // Load model & create prediction engine\r\n string modelP" + "ath = @\""); @@ -83,6 +93,8 @@ public static PredictionEngine CreatePredictionEngine() internal CSharp.GenerateTarget Target {get;set;} public bool HasNormalizeMapping {get; set;}=false; public bool HasLabelMapping {get; set;}=false; +public bool HasReshapeTransformer {get; set;}=false; +public bool HasObjectDetectionLabelMapping {get; set;}=false; public string MLNetModelpath {get; set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt index 30128af221..2b3b149adf 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt @@ -42,6 +42,14 @@ namespace <#= Namespace #>.Model // Register LabelMapping to map predicted Labels to their corresponding probabilities (likelihood of specified Labels) mlContext.ComponentCatalog.RegisterAssembly(typeof(LabelMapping).Assembly); <#} #> +<#if(HasReshapeTransformer){ #> + // Register ReshapeTransformer to calculate probabilities for each Label. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); +<#} #> +<#if(HasObjectDetectionLabelMapping){ #> + // Register ObjectDetectionMapping to map predicted labels to correct string. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionOnnxMapping).Assembly); +<#} #> // Load model & create prediction engine string modelPath = @"<#= MLNetModelpath #>"; @@ -57,5 +65,7 @@ public string Namespace {get;set;} internal CSharp.GenerateTarget Target {get;set;} public bool HasNormalizeMapping {get; set;}=false; public bool HasLabelMapping {get; set;}=false; +public bool HasReshapeTransformer {get; set;}=false; +public bool HasObjectDetectionLabelMapping {get; set;}=false; public string MLNetModelpath {get; set;} #> \ No newline at end of file diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs index aef6b95782..5c5d501c05 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs @@ -36,7 +36,7 @@ public virtual string TransformText() if("BinaryClassification".Equals(TaskType)){ this.Write(" // ColumnName attribute is used to change the column name from\r\n /" + "/ its default value, which is the name of the field.\r\n [ColumnName(\"Predi" + - "ctedLabel\")]\r\n public bool Prediction { get; set; }\r\n\r\n"); + "ctedLabel\")]\r\n public bool Prediction { get; set; }\r\n"); } if("MulticlassClassification".Equals(TaskType)){ this.Write(" // ColumnName attribute is used to change the column name from\r\n /" + "/ its default value, which is the name of the field.\r\n [ColumnName(\"Predi" + @@ -44,7 +44,12 @@ public virtual string TransformText() this.Write(this.ToStringHelper.ToStringWithCulture(PredictionLabelType)); this.Write(" Prediction { get; set; }\r\n"); } -if("MulticlassClassification".Equals(TaskType)){ +if("ObjectDetection".Equals(TaskType)){ + this.Write(" [ColumnName(\"boxes\")]\r\n public float[] Boxes { get; set; }\r\n\r\n " + + " [ColumnName(\"PredictedLabels)]\r\n public string[] Labels { get; set; }" + + "\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; set; }\r" + + "\n"); +} else if("MulticlassClassification".Equals(TaskType)){ this.Write(" public float[] Score { get; set; }\r\n"); }else{ this.Write(" public float Score { get; set; }\r\n"); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt index 728f81c3f8..4102973482 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt @@ -21,14 +21,22 @@ namespace <#= Namespace #>.Model // its default value, which is the name of the field. [ColumnName("PredictedLabel")] public bool Prediction { get; set; } - <# } if("MulticlassClassification".Equals(TaskType)){ #> // ColumnName attribute is used to change the column name from // its default value, which is the name of the field. [ColumnName("PredictedLabel")] public <#= PredictionLabelType#> Prediction { get; set; } <# }#> -<#if("MulticlassClassification".Equals(TaskType)){ #> +<#if("ObjectDetection".Equals(TaskType)){ #> + [ColumnName("boxes")] + public float[] Boxes { get; set; } + + [ColumnName("PredictedLabels)] + public string[] Labels { get; set; } + + [ColumnName("scores")] + public float[] Scores { get; set; } +<#} else if("MulticlassClassification".Equals(TaskType)){ #> public float[] Score { get; set; } <#}else{ #> public float Score { get; set; } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt new file mode 100644 index 0000000000..493be936be --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt new file mode 100644 index 0000000000..d2622aecf7 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -0,0 +1,23 @@ + + + + netstandard2.0 + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt new file mode 100644 index 0000000000..322ec6fafa --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt @@ -0,0 +1,43 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.ML; +using CodeGenTest.Model; + +namespace CodeGenTest.Model +{ + public class ConsumeModel + { + private static Lazy> PredictionEngine = new Lazy>(CreatePredictionEngine); + + // For more info on consuming ML.NET models, visit https://aka.ms/mlnet-consume + // Method for consuming model in your app + public static ModelOutput Predict(ModelInput input) + { + ModelOutput result = PredictionEngine.Value.Predict(input); + return result; + } + + public static PredictionEngine CreatePredictionEngine() + { + // Create new MLContext + MLContext mlContext = new MLContext(); + + // Register ReshapeTransformer to calculate probabilities for each Label. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); + + // Register ObjectDetectionMapping to map predicted labels to correct string. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionOnnxMapping).Assembly); + + // Load model & create prediction engine + string modelPath = @"C:\Users\Tevin\Desktop\Model"; + ITransformer mlModel = mlContext.Model.Load(modelPath, out var modelInputSchema); + var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); + + return predEngine; + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt new file mode 100644 index 0000000000..d9645c8849 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt @@ -0,0 +1,104 @@ +// This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace CodeGenTest.Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[] { "label1", "label2", "label3", }; + + public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + { + long[] oldLabels = input.Labels.GetValues().ToArray(); + output.Labels = new string[oldLabels.Length]; + output.Scores = input.Scores.GetValues().ToArray(); + output.Boxes = input.Boxes.GetValues().ToArray(); + for (int i = 0; i < oldLabels.Length; i++) + { + output.Labels[i] = Label[oldLabels[i]]; + } + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class ObjectDetectionLabelMappingInput + { + [ColumnName("labels")] + public VBuffer Labels; + + [ColumnName("scores")] + public VBuffer Scores; + + [ColumnName("boxes")] + public VBuffer Boxes; + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } + + + public class ModelOutput + { + [ColumnName("boxes")] + public float[] Boxes; + + [ColumnName("PredictedLabels")] + public string[] Labels; + + [ColumnName("scores")] + public float[] Scores; + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + return boundingBoxes.ToArray(); + } + } + + public BoundingBox[] GetBoundingBoxes() + { + return this.BoundingBoxes; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var box in this.BoundingBoxes) + { + sb.AppendLine(box.ToString()); + } + + return sb.ToString(); + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt new file mode 100644 index 0000000000..1dbc8ac7e4 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -0,0 +1,79 @@ +// This file was auto-generated by ML.NET Model Builder. +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.ML; +using Microsoft.ML.Data; +using CodeGenTest.Model; +namespace CodeGenTest.ConsoleApp +{ + public static class ModelBuilder + { + private static string TRAIN_DATA_FILEPATH = @"C:\Users\Tevin\Downloads\ODCSV.csv"; + private static string MLNET_MODEL = @"C:\Users\Tevin\Desktop\Model"; + private static string ONNX_MODEL = @"C:\Users\Tevin\Desktop\bestModel.onnx"; + + // Create MLContext to be shared across the model creation workflow objects + // Set a random seed for repeatable/deterministic results across multiple trainings. + private static MLContext mlContext = new MLContext(seed: 1); + + // Training in Azure produces an ONNX model; this method demonstrates creating an ML.NET model (MLModel.zip) from the ONNX model (bestModel.onnx), which you can then use with the ConsumeModel() method to make predictions + public static void CreateMLNetModelFromOnnx() + { + // Load data + IDataView inputDataView = mlContext.Data.LoadFromTextFile( + path: TRAIN_DATA_FILEPATH, + hasHeader: true, + separatorChar: '\t', + allowQuoting: true, + allowSparse: true); + + // Create an ML.NET pipeline to score using the ONNX model + // Notice that this pipeline is not trainable because it only contains transformers + IEstimator pipeline = BuildPipeline(mlContext); + + // Create ML.NET model from pipeline + ITransformer mlModel = pipeline.Fit(inputDataView); + + // Save model + SaveModel(mlContext, mlModel, MLNET_MODEL, inputDataView.Schema); + } + + public static IEstimator BuildPipeline(MLContext mlContext) + { + // Data process configuration with pipeline data transformations to: + // 1. Score using provided onnx model + // 2. Map scores to labels to make model output easier to understand and use + var pipeline = mlContext.Transforms.LoadImages("C:\Users\Tevin\Desktop\bestModel.onnx", null, "ImagePath") + .Append(mlContext.Transforms.ResizeImages("ImageSource_featurized", 224, 224, "ImageSource_featurized")) + .Append(mlContext.Transforms.ExtractPixels("ImageSource_featurized", "ImageSource_featurized")) + .Append(mlContext.Transforms.CustomMapping( + (input, output) => ReshapeTransformer.Mapping(input, output), + contractName: nameof(ReshapeTransformer))) + .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL)) + .Append(mlContext.Transforms.CustomMapping( // added this line not sure what to do. + (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), + contractName: nameof(ObjectDetectionLabelMapping))); + return pipeline; + } + + private static void SaveModel(MLContext mlContext, ITransformer mlModel, string modelRelativePath, DataViewSchema modelInputSchema) + { + // Save/persist the trained model to a .ZIP file + Console.WriteLine($"=============== Saving the model ==============="); + mlContext.Model.Save(mlModel, modelInputSchema, GetAbsolutePath(modelRelativePath)); + Console.WriteLine("The model is saved to {0}", GetAbsolutePath(modelRelativePath)); + } + + public static string GetAbsolutePath(string relativePath) + { + FileInfo _dataRoot = new FileInfo(typeof(Program).Assembly.Location); + string assemblyFolderPath = _dataRoot.Directory.FullName; + + string fullPath = Path.Combine(assemblyFolderPath, relativePath); + + return fullPath; + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelInput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelInput.cs.approved.txt new file mode 100644 index 0000000000..41cf93f623 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelInput.cs.approved.txt @@ -0,0 +1,18 @@ +// This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; + +namespace CodeGenTest.Model +{ + public class ModelInput + { + [ColumnName("Label"), LoadColumn(0)] + public string Label { get; set; } + + + [ColumnName("ImagePath"), LoadColumn(1)] + public string ImagePath { get; set; } + + + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt new file mode 100644 index 0000000000..fcbee38baa --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -0,0 +1,19 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using Microsoft.ML.Data; + +namespace CodeGenTest.Model +{ + public class ModelOutput + { + [ColumnName("boxes")] + public float[] Boxes { get; set; } + + [ColumnName("PredictedLabels)] + public string[] Labels { get; set; } + + [ColumnName("scores")] + public float[] Scores { get; set; } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt new file mode 100644 index 0000000000..4687096197 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt @@ -0,0 +1,50 @@ +// This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace CodeGenTest.Model +{ + [CustomMappingFactoryAttribute(nameof(NormalizeMapping))] + public class NormalizeMapping : CustomMappingFactory + { + // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. + public static void Mapping(NormalizeInput input, NormalizeOutput output) + { + var values = input.Reshape.GetValues().ToArray(); + + var image_mean = new float[] { 0.485f, 0.456f, 0.406f }; + var image_std = new float[] { 0.229f, 0.224f, 0.225f }; + + for (int x = 0; x < values.Count(); x++) + { + var y = x % 3; + // Normalize by 255 first + values[x] /= 255; + values[x] = (values[x] - image_mean[y]) / image_std[y]; + }; + + output.Reshape = new VBuffer(values.Count(), values); + } + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + public class NormalizeInput + { + [ColumnName("ImageSource_featurized")] + [VectorType(3, 224, 224)] + public VBuffer Reshape; + } + public class NormalizeOutput + { + [ColumnName("input1")] + [VectorType(3 * 224 * 224)] + public VBuffer Reshape; + } +} + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt new file mode 100644 index 0000000000..b9ce8321ae --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt @@ -0,0 +1,27 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using CodeGenTest.Model; + +namespace CodeGenTest.ConsoleApp +{ + class Program + { + static void Main(string[] args) + { + // Create single instance of sample data from first line of dataset for model input + ModelInput sampleData = new ModelInput() + { + ImagePath = null, + }; + + // Make a single prediction on the sample data and print results + var predictionResult = ConsumeModel.Predict(sampleData); + + Console.WriteLine("Using model to make single prediction -- Comparing actual Label with predicted Label from sample data...\n\n"); + Console.WriteLine($"ImagePath: {sampleData.ImagePath}"); + Console.WriteLine("=============== End of process, hit any key to finish ==============="); + Console.ReadKey(); + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt new file mode 100644 index 0000000000..43f19aa68e --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt @@ -0,0 +1,48 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using System.Linq; +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; + +namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers +{ + [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] + class ReshapeTransformer : CustomMappingFactory + { + // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. + public static void Mapping(Input input, Output output) + { + var values = input.Reshape.GetValues().ToArray(); + + for (int x = 0; x < values.Count(); x++) + { + // normalize from [0, 255] to [0, 1] + values[x] /= 255; + }; + + output.Reshape = new VBuffer(values.Count(), values); + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class Input + { + [ColumnName("input")] + [VectorType(3, 800, 600)] + public VBuffer Reshape; + } + + public class Output + { + [ColumnName("input")] + [VectorType(1 * 3 * 600 * 800)] + public VBuffer Reshape; + } +} + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionCSFileContentTest.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionCSFileContentTest.approved.txt index ab96c8c8d9..c87448f3da 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionCSFileContentTest.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.PredictionCSFileContentTest.approved.txt @@ -15,7 +15,6 @@ namespace TestNamespace.Model // its default value, which is the name of the field. [ColumnName("PredictedLabel")] public bool Prediction { get; set; } - public float Score { get; set; } } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index c7539dddd6..b6089dca03 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -228,6 +228,43 @@ public void AzureImageCodeGeneratorTest() } + // Tevin: added to test OD codeGen + [Fact] + [UseReporter(typeof(DiffReporter))] + [MethodImpl(MethodImplOptions.NoInlining)] + public void AzureObjectDetectionCodeGeneratorTest() + { + string onnxPath = @"C:\Users\Tevin\Desktop\bestModel.onnx"; + (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(onnxPath); + var setting = new CodeGeneratorSettings() + { + TrainDataset = @"C:\Users\Tevin\Downloads\ODCSV.csv", + ModelPath = @"C:\Users\Tevin\Desktop\Model", + MlTask = TaskKind.ObjectDetection, + OutputName = @"CodeGenTest", + OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", + LabelName = "Label", + Target = GenerateTarget.ModelBuilder, + StablePackageVersion = "stableversion", + UnstablePackageVersion = "unstableversion", + OnnxModelPath = onnxPath, + IsAzureAttach = true, + IsImage = false, + IsObjectDetection = true, + ClassificationLabel = new string[] { "label1", "label2", "label3" }, + }; + var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); + foreach (var project in codeGen.ToSolution()) + { + foreach (var projectFile in project) + { + NamerFactory.AdditionalInformation = projectFile.Name; + Approvals.Verify(((ICSharpFile)projectFile).File); + } + } + } + + [Fact] [UseReporter(typeof(DiffReporter))] @@ -596,6 +633,64 @@ private CodeGenerator PrepareForRecommendationTask() return (_mockedPipeline, _columnInference); } + private (Pipeline, ColumnInferenceResults) GetMockedAzureObjectDetectionPipelineAndInference(string onnxModelPath) + { + var onnxPipeLineNode = new PipelineNode( + nameof(SpecialTransformer.ApplyOnnxModel), + PipelineNodeType.Transform, + new[] { "input" }, + new[] { "boxes", "labels", "scores" }, + new Dictionary() + { + { "outputColumnNames", new List() { "boxes", "labels", "scores" } }, + { "inputColumnNames", "input" }, + { "modelFile", "awesomeModel.onnx" }, // it doesn't matter what modelFile is + }); + var loadImageNode = new PipelineNode(EstimatorName.ImageLoading.ToString(), PipelineNodeType.Transform, "ImagePath", onnxModelPath); + var resizeImageNode = new PipelineNode( + nameof(SpecialTransformer.ResizeImage), + PipelineNodeType.Transform, + onnxModelPath, + onnxModelPath, + new Dictionary() + { + { "imageWidth", 800 }, + { "imageHeight", 600 }, + }); + var extractPixelsNode = new PipelineNode(nameof(SpecialTransformer.ExtractPixel), PipelineNodeType.Transform, onnxModelPath, onnxModelPath); + var normalizeMapping = new PipelineNode(nameof(SpecialTransformer.ReshapeTransformer), PipelineNodeType.Transform, string.Empty, string.Empty); + var labelMapping = new PipelineNode(nameof(SpecialTransformer.ObjectDetectionLabelMapping), PipelineNodeType.Transform, string.Empty, string.Empty); + var bestPipeLine = new Pipeline(new PipelineNode[] + { + loadImageNode, + resizeImageNode, + extractPixelsNode, + normalizeMapping, + onnxPipeLineNode, + labelMapping, + }); + + var textLoaderArgs = new TextLoader.Options() + { + Columns = new[] { + new TextLoader.Column("Label", DataKind.String, 0), + new TextLoader.Column("ImagePath", DataKind.String, 1), // 0? + }, + AllowQuoting = true, + AllowSparse = true, + HasHeader = true, + Separators = new[] { '\t' } + }; + + var columnInference = new ColumnInferenceResults() + { + TextLoaderOptions = textLoaderArgs, + ColumnInformation = new ColumnInformation() { LabelColumnName = "Label" } + }; + + return (bestPipeLine, columnInference); + } + private (Pipeline, ColumnInferenceResults) GetMockedAzureImagePipelineAndInference() { // construct pipeline From 8cedee57f46d530da4c45a4e2c0d27ea8fc03c77 Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 2 Jun 2020 16:41:09 -0700 Subject: [PATCH 06/28] updated testing files to give better test results --- ...etectionCodeGeneratorTest.ConsumeModel.cs.approved.txt | 2 +- ...etectionCodeGeneratorTest.ModelBuilder.cs.approved.txt | 8 ++++---- ...jectDetectionCodeGeneratorTest.Program.cs.approved.txt | 6 +----- .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 8 ++++---- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt index 322ec6fafa..56cf9d9a2d 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt @@ -33,7 +33,7 @@ namespace CodeGenTest.Model mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionOnnxMapping).Assembly); // Load model & create prediction engine - string modelPath = @"C:\Users\Tevin\Desktop\Model"; + string modelPath = @"/path/to/model"; ITransformer mlModel = mlContext.Model.Load(modelPath, out var modelInputSchema); var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 1dbc8ac7e4..888b6d9d54 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -10,9 +10,9 @@ namespace CodeGenTest.ConsoleApp { public static class ModelBuilder { - private static string TRAIN_DATA_FILEPATH = @"C:\Users\Tevin\Downloads\ODCSV.csv"; - private static string MLNET_MODEL = @"C:\Users\Tevin\Desktop\Model"; - private static string ONNX_MODEL = @"C:\Users\Tevin\Desktop\bestModel.onnx"; + private static string TRAIN_DATA_FILEPATH = @"/path/to/dataset"; + private static string MLNET_MODEL = @"/path/to/model"; + private static string ONNX_MODEL = @"/path/to/onnxModel"; // Create MLContext to be shared across the model creation workflow objects // Set a random seed for repeatable/deterministic results across multiple trainings. @@ -45,7 +45,7 @@ namespace CodeGenTest.ConsoleApp // Data process configuration with pipeline data transformations to: // 1. Score using provided onnx model // 2. Map scores to labels to make model output easier to understand and use - var pipeline = mlContext.Transforms.LoadImages("C:\Users\Tevin\Desktop\bestModel.onnx", null, "ImagePath") + var pipeline = mlContext.Transforms.LoadImages("/path/to/onnxModel", null, "ImagePath") .Append(mlContext.Transforms.ResizeImages("ImageSource_featurized", 224, 224, "ImageSource_featurized")) .Append(mlContext.Transforms.ExtractPixels("ImageSource_featurized", "ImageSource_featurized")) .Append(mlContext.Transforms.CustomMapping( diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt index b9ce8321ae..43d3fe51fc 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt @@ -10,16 +10,12 @@ namespace CodeGenTest.ConsoleApp static void Main(string[] args) { // Create single instance of sample data from first line of dataset for model input - ModelInput sampleData = new ModelInput() - { - ImagePath = null, - }; + ModelInput sampleData = new ModelInput(); // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); Console.WriteLine("Using model to make single prediction -- Comparing actual Label with predicted Label from sample data...\n\n"); - Console.WriteLine($"ImagePath: {sampleData.ImagePath}"); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index b6089dca03..00bf50a815 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -234,15 +234,15 @@ public void AzureImageCodeGeneratorTest() [MethodImpl(MethodImplOptions.NoInlining)] public void AzureObjectDetectionCodeGeneratorTest() { - string onnxPath = @"C:\Users\Tevin\Desktop\bestModel.onnx"; + string onnxPath = @"/path/to/onnxModel"; (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(onnxPath); var setting = new CodeGeneratorSettings() { - TrainDataset = @"C:\Users\Tevin\Downloads\ODCSV.csv", - ModelPath = @"C:\Users\Tevin\Desktop\Model", + TrainDataset = @"/path/to/dataset", + ModelPath = @"/path/to/model", MlTask = TaskKind.ObjectDetection, OutputName = @"CodeGenTest", - OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", + OutputBaseDir = @"/path/to/codegen", LabelName = "Label", Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", From 44f18efd52e5342ab9938956fa88210c5d6d763b Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 2 Jun 2020 17:32:41 -0700 Subject: [PATCH 07/28] Refactored test code --- .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 00bf50a815..30654ebd80 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -234,8 +234,7 @@ public void AzureImageCodeGeneratorTest() [MethodImpl(MethodImplOptions.NoInlining)] public void AzureObjectDetectionCodeGeneratorTest() { - string onnxPath = @"/path/to/onnxModel"; - (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(onnxPath); + (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(@"/path/to/onnxModel"); var setting = new CodeGeneratorSettings() { TrainDataset = @"/path/to/dataset", @@ -247,7 +246,7 @@ public void AzureObjectDetectionCodeGeneratorTest() Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", UnstablePackageVersion = "unstableversion", - OnnxModelPath = onnxPath, + OnnxModelPath = @"/path/to/onnxModel", IsAzureAttach = true, IsImage = false, IsObjectDetection = true, From eed5d8542532019becb0d3d5f8032b17defdf7a1 Mon Sep 17 00:00:00 2001 From: Tevin Date: Wed, 3 Jun 2020 09:57:10 -0700 Subject: [PATCH 08/28] trying to test performance --- .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 30654ebd80..49b5d1e420 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -228,7 +228,7 @@ public void AzureImageCodeGeneratorTest() } - // Tevin: added to test OD codeGen + // Tevin: added to test OD codeGen working [Fact] [UseReporter(typeof(DiffReporter))] [MethodImpl(MethodImplOptions.NoInlining)] From 045f93a4525d10f2a3661988cd3397fd98197731 Mon Sep 17 00:00:00 2001 From: Tevin Date: Mon, 8 Jun 2020 16:10:18 -0700 Subject: [PATCH 09/28] fix commit --- .../AzureAttachModelCodeGenerator.cs | 2 +- .../CSharp/TransformGeneratorFactory.cs | 12 +++++ .../CSharp/TransformGenerators.cs | 40 +++++++++++++-- .../Azure/Model/ObjectDetectionOnnxMapping.cs | 24 ++++----- .../Azure/Model/ObjectDetectionOnnxMapping.tt | 2 +- .../Azure/Model/ReshapeTransformer.cs | 2 +- .../Azure/Model/ReshapeTransformer.tt | 2 +- .../Templates/Console/ModelOutputClass.cs | 25 ++++++++-- .../Templates/Console/ModelOutputClass.tt | 49 ++++++++++++++++++- .../Templates/Console/ModelProject.cs | 3 +- .../Templates/Console/ModelProject.tt | 1 + .../Templates/Console/PredictProject.cs | 3 +- .../Templates/Console/PredictProject.tt | 1 + ...CodeGenTest.ConsoleApp.csproj.approved.txt | 1 + ...Test.CodeGenTest.Model.csproj.approved.txt | 1 + ...GeneratorTest.ModelBuilder.cs.approved.txt | 10 ++-- ...eGeneratorTest.ModelOutput.cs.approved.txt | 49 ++++++++++++++++++- ...torTest.ReshapeTransformer.cs.approved.txt | 2 +- .../ConsoleCodeGeneratorTests.cs | 8 +-- 19 files changed, 201 insertions(+), 36 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index 71f7b9ebb0..d4060c715c 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -131,7 +131,7 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c Namespace = _nameSpaceValue, ImportLabels = _settings.ClassificationLabel, }.TransformText(), - Name = "LabelMapping.cs", + Name = "ObjectDetectionLabelMapping.cs", }; ReshapeTransformerMap = new CSharpCodeFile() diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs index e9ff0b90e7..6f00f1e9da 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs @@ -24,6 +24,9 @@ internal enum SpecialTransformer LabelMapping = 4, ObjectDetectionLabelMapping = 5, // Tevin: added for OD mappings ReshapeTransformer = 6, + ObjectDetectionResizeImage = 7, + ObjectDetectionExtractPixel = 8, + ApplyObjectDetectionModel = 9, } internal static class TransformGeneratorFactory @@ -103,6 +106,15 @@ internal static ITransformGenerator GetInstance(PipelineNode node) case SpecialTransformer.ObjectDetectionLabelMapping: result = new CustomObjectDetectionLabelMapping(node); break; + case SpecialTransformer.ObjectDetectionExtractPixel: + result = new ObjectDetectionPixelExtract(node); + break; + case SpecialTransformer.ObjectDetectionResizeImage: + result = new ObjectDetectionImageResizing(node); + break; + case SpecialTransformer.ApplyObjectDetectionModel: + result = new ApplyObjectDetectionOnnxModel(node); + break; case SpecialTransformer.ReshapeTransformer: result = new CustomReshapeTransformer(node); break; diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index 5a8c10323e..17831fd8cc 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -377,6 +377,17 @@ public override string GenerateTransformer() } } + internal class ObjectDetectionImageResizing : TransformGeneratorBase + { + public ObjectDetectionImageResizing(PipelineNode node) : base(node) { } + internal override string MethodName => "ResizeImages"; + + public override string GenerateTransformer() + { + return @"ResizeImages(outputColumnName: ""input"", imageWidth: 800, imageHeight: 600, inputColumnName: ""input"")"; + } + } + internal class PixelExtract : TransformGeneratorBase { public PixelExtract(PipelineNode node) : base(node) { } @@ -388,7 +399,18 @@ public override string GenerateTransformer() } } - internal class CustomNormalizeMapping : TransformGeneratorBase + internal class ObjectDetectionPixelExtract : TransformGeneratorBase + { + public ObjectDetectionPixelExtract(PipelineNode node) : base(node) { } + internal override string MethodName => "ExtractPixels"; + + public override string GenerateTransformer() + { + return @"ExtractPixels(outputColumnName: ""input"", inputColumnName: ""input"")"; + } + } + +internal class CustomNormalizeMapping : TransformGeneratorBase { public CustomNormalizeMapping(PipelineNode node) : base(node) { } internal override string MethodName => "NormalizeMapping"; @@ -422,7 +444,7 @@ public CustomObjectDetectionLabelMapping(PipelineNode node) : base(node) { } public override string GenerateTransformer() { - return @"CustomMapping( // added this line not sure what to do. + return @"CustomMapping( // added this line not sure what to do. (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), contractName: nameof(ObjectDetectionLabelMapping))"; } @@ -432,7 +454,7 @@ public override string GenerateTransformer() internal class CustomReshapeTransformer : TransformGeneratorBase { public CustomReshapeTransformer(PipelineNode node) : base(node) { } - internal override string MethodName => "ObjectDetectionLabelMapping"; + internal override string MethodName => "ReshapeTransformer"; public override string GenerateTransformer() { @@ -442,6 +464,18 @@ public override string GenerateTransformer() } } + // Tevin: added to have correct ApplyOnnxModel + internal class ApplyObjectDetectionOnnxModel : TransformGeneratorBase + { + public ApplyObjectDetectionOnnxModel(PipelineNode node) : base(node) { } + internal override string MethodName => "ApplyOnnxModel"; + + public override string GenerateTransformer() + { + return @"ApplyOnnxModel(modelFile: modelFile, outputColumnNames: new[] { ""boxes"",""labels"", ""scores"" }, inputColumnNames: new[] { ""input"" })"; + } + } + internal class ApplyOnnxModel : TransformGeneratorBase { public ApplyOnnxModel(PipelineNode node) : base(node) { } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs index 5081008baf..99fbea5ff9 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs @@ -62,18 +62,18 @@ public class ObjectDetectionLabelMapping : CustomMappingFactory();\r\n return bound" + - "ingBoxes.ToArray();\r\n }\r\n }\r\n\r\n public BoundingBox[] Ge" + - "tBoundingBoxes()\r\n {\r\n return this.BoundingBoxes;\r\n }\r\n" + - "\r\n public override string ToString()\r\n {\r\n var sb = new" + - " StringBuilder();\r\n\r\n foreach (var box in this.BoundingBoxes)\r\n " + - " {\r\n sb.AppendLine(box.ToString());\r\n }\r\n\r\n " + - " return sb.ToString();\r\n }\r\n }\r\n}\r\n"); + "{this.Label}, Score: {this.Score}\";\r\n }\r\n }\r\n\r\n\r\n public class Obje" + + "ctDetectionLabelMappingOutput\r\n {\r\n [ColumnName(\"boxes\")]\r\n pub" + + "lic float[] Boxes;\r\n\r\n [ColumnName(\"PredictedLabels\")]\r\n public st" + + "ring[] Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores;" + + "\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n get\r\n " + + " {\r\n var boundingBoxes = new List();\r\n " + + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n p" + + "ublic BoundingBox[] GetBoundingBoxes()\r\n {\r\n return this.Bound" + + "ingBoxes;\r\n }\r\n\r\n public override string ToString()\r\n {\r\n " + + " var sb = new StringBuilder();\r\n\r\n foreach (var box in this." + + "BoundingBoxes)\r\n {\r\n sb.AppendLine(box.ToString());\r\n " + + " }\r\n\r\n return sb.ToString();\r\n }\r\n }\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt index b521f8c0e3..7a4aa14fc2 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt @@ -73,7 +73,7 @@ namespace <#= Namespace #>.Model } - public class ModelOutput + public class ObjectDetectionLabelMappingOutput { [ColumnName("boxes")] public float[] Boxes; diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs index 9c214842b0..7b6506ba24 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs @@ -39,7 +39,7 @@ public virtual string TransformText() namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - class ReshapeTransformer : CustomMappingFactory + public class ReshapeTransformer : CustomMappingFactory { // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. public static void Mapping(Input input, Output output) diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt index 270496ba71..d69e9a38b7 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt @@ -17,7 +17,7 @@ using Microsoft.ML.Transforms; namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - class ReshapeTransformer : CustomMappingFactory + public class ReshapeTransformer : CustomMappingFactory { // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. public static void Mapping(Input input, Output output) diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs index 5c5d501c05..2332f21c00 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs @@ -46,9 +46,28 @@ public virtual string TransformText() } if("ObjectDetection".Equals(TaskType)){ this.Write(" [ColumnName(\"boxes\")]\r\n public float[] Boxes { get; set; }\r\n\r\n " + - " [ColumnName(\"PredictedLabels)]\r\n public string[] Labels { get; set; }" + - "\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; set; }\r" + - "\n"); + " [ColumnName(\"PredictedLabels\")]\r\n public string[] Labels { get; set; " + + "}\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; set; }" + + "\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n get\r\n " + + " {\r\n var boundingBoxes = new List();\r\n\r\n " + + " boundingBoxes = Enumerable.Range(0, this.Labels.Length)\r\n " + + " .Select((index) =>\r\n {\r\n " + + " var boxes = this.Boxes;\r\n var scores " + + "= this.Scores;\r\n var labels = this.Labels;\r\n\r\n " + + " return new BoundingBox()\r\n " + + " {\r\n Left = boxes[index * 4],\r\n " + + " Top = boxes[(index * 4) + 1],\r\n " + + " Right = boxes[(index * 4) + 2],\r\n Bottom = b" + + "oxes[(index * 4) + 3],\r\n Score = scores[index]," + + "\r\n Label = labels[index].ToString(),\r\n " + + " };\r\n }).ToList();\r\n " + + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n }\r\n\r\n public " + + "class BoundingBox\r\n {\r\n public float Top;\r\n\r\n public float Left" + + ";\r\n\r\n public float Right;\r\n\r\n public float Bottom;\r\n\r\n publ" + + "ic string Label;\r\n\r\n public float Score;\r\n\r\n public override strin" + + "g ToString()\r\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}" + + ", Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this." + + "Score}\";\r\n }\r\n"); } else if("MulticlassClassification".Equals(TaskType)){ this.Write(" public float[] Score { get; set; }\r\n"); }else{ diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt index 4102973482..eb45998467 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt @@ -31,11 +31,58 @@ namespace <#= Namespace #>.Model [ColumnName("boxes")] public float[] Boxes { get; set; } - [ColumnName("PredictedLabels)] + [ColumnName("PredictedLabels")] public string[] Labels { get; set; } [ColumnName("scores")] public float[] Scores { get; set; } + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + + boundingBoxes = Enumerable.Range(0, this.Labels.Length) + .Select((index) => + { + var boxes = this.Boxes; + var scores = this.Scores; + var labels = this.Labels; + + return new BoundingBox() + { + Left = boxes[index * 4], + Top = boxes[(index * 4) + 1], + Right = boxes[(index * 4) + 2], + Bottom = boxes[(index * 4) + 3], + Score = scores[index], + Label = labels[index].ToString(), + }; + }).ToList(); + return boundingBoxes.ToArray(); + } + } + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } <#} else if("MulticlassClassification".Equals(TaskType)){ #> public float[] Score { get; set; } <#}else{ #> diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs index 9a95bb0e64..e5c018b56f 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs @@ -53,7 +53,8 @@ public virtual string TransformText() if (IncludeOnnxModel){ this.Write(" \r\n"); + this.Write("\" />\r\n " + + "\r\n"); } if (IncludeImageClassificationPackage){ this.Write(" <# if (IncludeOnnxModel){ #> + <#}#> <# if (IncludeImageClassificationPackage){ #> diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs index 29f288a076..24697b4b7b 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs @@ -54,7 +54,8 @@ public virtual string TransformText() if (IncludeOnnxPackage){ this.Write(" \r\n"); + this.Write("\" />\r\n " + + "\r\n"); } if (IncludeResNet18Package){ this.Write(" + <#}#> <# if (IncludeResNet18Package){ #> diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index 493be936be..86413329df 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -7,6 +7,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt index d2622aecf7..b5f9ed0d42 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -6,6 +6,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 888b6d9d54..9627604a25 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -45,14 +45,14 @@ namespace CodeGenTest.ConsoleApp // Data process configuration with pipeline data transformations to: // 1. Score using provided onnx model // 2. Map scores to labels to make model output easier to understand and use - var pipeline = mlContext.Transforms.LoadImages("/path/to/onnxModel", null, "ImagePath") - .Append(mlContext.Transforms.ResizeImages("ImageSource_featurized", 224, 224, "ImageSource_featurized")) - .Append(mlContext.Transforms.ExtractPixels("ImageSource_featurized", "ImageSource_featurized")) + var pipeline = mlContext.Transforms.LoadImages("input", null, "ImagePath") + .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: 800, imageHeight: 600, inputColumnName: "input")) + .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", inputColumnName: "input")) .Append(mlContext.Transforms.CustomMapping( (input, output) => ReshapeTransformer.Mapping(input, output), contractName: nameof(ReshapeTransformer))) - .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL)) - .Append(mlContext.Transforms.CustomMapping( // added this line not sure what to do. + .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: modelFile, outputColumnNames: new[] { "boxes", "labels", "scores" }, inputColumnNames: new[] { "input" })) + .Append(mlContext.Transforms.CustomMapping( // added this line not sure what to do. (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), contractName: nameof(ObjectDetectionLabelMapping))); return pipeline; diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt index fcbee38baa..8f6db0f9d3 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -10,10 +10,57 @@ namespace CodeGenTest.Model [ColumnName("boxes")] public float[] Boxes { get; set; } - [ColumnName("PredictedLabels)] + [ColumnName("PredictedLabels")] public string[] Labels { get; set; } [ColumnName("scores")] public float[] Scores { get; set; } + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + + boundingBoxes = Enumerable.Range(0, this.Labels.Length) + .Select((index) => + { + var boxes = this.Boxes; + var scores = this.Scores; + var labels = this.Labels; + + return new BoundingBox() + { + Left = boxes[index * 4], + Top = boxes[(index * 4) + 1], + Right = boxes[(index * 4) + 2], + Bottom = boxes[(index * 4) + 3], + Score = scores[index], + Label = labels[index].ToString(), + }; + }).ToList(); + return boundingBoxes.ToArray(); + } + } + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt index 43f19aa68e..e7a97589f2 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt @@ -8,7 +8,7 @@ using Microsoft.ML.Transforms; namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - class ReshapeTransformer : CustomMappingFactory + public class ReshapeTransformer : CustomMappingFactory { // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. public static void Mapping(Input input, Output output) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 49b5d1e420..1396e77745 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -635,7 +635,7 @@ private CodeGenerator PrepareForRecommendationTask() private (Pipeline, ColumnInferenceResults) GetMockedAzureObjectDetectionPipelineAndInference(string onnxModelPath) { var onnxPipeLineNode = new PipelineNode( - nameof(SpecialTransformer.ApplyOnnxModel), + nameof(SpecialTransformer.ApplyObjectDetectionModel), PipelineNodeType.Transform, new[] { "input" }, new[] { "boxes", "labels", "scores" }, @@ -645,9 +645,9 @@ private CodeGenerator PrepareForRecommendationTask() { "inputColumnNames", "input" }, { "modelFile", "awesomeModel.onnx" }, // it doesn't matter what modelFile is }); - var loadImageNode = new PipelineNode(EstimatorName.ImageLoading.ToString(), PipelineNodeType.Transform, "ImagePath", onnxModelPath); + var loadImageNode = new PipelineNode(EstimatorName.ImageLoading.ToString(), PipelineNodeType.Transform, "ImagePath", "input"); var resizeImageNode = new PipelineNode( - nameof(SpecialTransformer.ResizeImage), + nameof(SpecialTransformer.ObjectDetectionResizeImage), PipelineNodeType.Transform, onnxModelPath, onnxModelPath, @@ -656,7 +656,7 @@ private CodeGenerator PrepareForRecommendationTask() { "imageWidth", 800 }, { "imageHeight", 600 }, }); - var extractPixelsNode = new PipelineNode(nameof(SpecialTransformer.ExtractPixel), PipelineNodeType.Transform, onnxModelPath, onnxModelPath); + var extractPixelsNode = new PipelineNode(nameof(SpecialTransformer.ObjectDetectionExtractPixel), PipelineNodeType.Transform, onnxModelPath, onnxModelPath); var normalizeMapping = new PipelineNode(nameof(SpecialTransformer.ReshapeTransformer), PipelineNodeType.Transform, string.Empty, string.Empty); var labelMapping = new PipelineNode(nameof(SpecialTransformer.ObjectDetectionLabelMapping), PipelineNodeType.Transform, string.Empty, string.Empty); var bestPipeLine = new Pipeline(new PipelineNode[] From c164c97c06a3d6b950ffaace163442876ac2fe07 Mon Sep 17 00:00:00 2001 From: Tevin Date: Mon, 8 Jun 2020 17:01:11 -0700 Subject: [PATCH 10/28] Adding changes from prior commit --- .../AzureAttachConsoleAppCodeGenerator.cs | 1 + .../AzureAttachModelCodeGenerator.cs | 1 + .../Templates/Console/ModelProject.cs | 7 +- .../Templates/Console/ModelProject.tt | 3 + .../Templates/Console/PredictProject.cs | 7 +- .../Templates/Console/PredictProject.tt | 3 + ...bjectDetectionLabelMapping.cs.approved.txt | 104 ++++++++++++++++++ 7 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs index 081fc5ef65..1f794bd167 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs @@ -71,6 +71,7 @@ public AzureAttachConsoleAppCodeGenerator(Pipeline pipeline, ColumnInferenceResu IncludeImageTransformerPackage = _settings.IsImage, IncludeImageClassificationPackage = false, IncludeOnnxPackage = true, + IncludeOnnxRuntime = _settings.IsObjectDetection, IncludeResNet18Package = false, IncludeRecommenderPackage = false, StablePackageVersion = _settings.StablePackageVersion, diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index d4060c715c..d3f90d696e 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -90,6 +90,7 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c IncludeLightGBMPackage = false, IncludeMklComponentsPackage = false, IncludeOnnxModel = true, + IncludeOnnxRuntime = _settings.IsObjectDetection, IncludeRecommenderPackage = false, StablePackageVersion = _settings.StablePackageVersion, UnstablePackageVersion = _settings.UnstablePackageVersion, diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs index e5c018b56f..11a15048df 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs @@ -53,8 +53,10 @@ public virtual string TransformText() if (IncludeOnnxModel){ this.Write(" \r\n " + - "\r\n"); + this.Write("\" />\r\n"); +} + if (IncludeOnnxRuntime){ + this.Write(" \r\n"); } if (IncludeImageClassificationPackage){ this.Write(" <# if (IncludeOnnxModel){ #> +<#}#> +<# if (IncludeOnnxRuntime){ #> <#}#> <# if (IncludeImageClassificationPackage){ #> @@ -61,6 +63,7 @@ public bool IncludeFastTreePackage {get;set;} public bool IncludeImageTransformerPackage {get; set;} public bool IncludeImageClassificationPackage {get; set;} public bool IncludeOnnxModel {get; set;} +public bool IncludeOnnxRuntime {get; set;} public bool IncludeRecommenderPackage {get;set;} public string StablePackageVersion {get;set;} public string UnstablePackageVersion {get;set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs index 24697b4b7b..e2167dbbec 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs @@ -54,8 +54,10 @@ public virtual string TransformText() if (IncludeOnnxPackage){ this.Write(" \r\n " + - "\r\n"); + this.Write("\" />\r\n"); +} + if (IncludeOnnxRuntime){ + this.Write(" \r\n"); } if (IncludeResNet18Package){ this.Write(" +<#}#> +<# if (IncludeOnnxRuntime){ #> <#}#> <# if (IncludeResNet18Package){ #> @@ -59,6 +61,7 @@ public bool IncludeFastTreePackage {get;set;} public bool IncludeImageTransformerPackage {get; set;} public bool IncludeImageClassificationPackage {get; set;} public bool IncludeOnnxPackage {get; set;} +public bool IncludeOnnxRuntime {get; set;} public bool IncludeResNet18Package {get; set;} public bool IncludeRecommenderPackage {get;set;} public string StablePackageVersion {get;set;} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt new file mode 100644 index 0000000000..862aa53ab8 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt @@ -0,0 +1,104 @@ +// This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace CodeGenTest.Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[] { "label1", "label2", "label3", }; + + public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + { + long[] oldLabels = input.Labels.GetValues().ToArray(); + output.Labels = new string[oldLabels.Length]; + output.Scores = input.Scores.GetValues().ToArray(); + output.Boxes = input.Boxes.GetValues().ToArray(); + for (int i = 0; i < oldLabels.Length; i++) + { + output.Labels[i] = Label[oldLabels[i]]; + } + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class ObjectDetectionLabelMappingInput + { + [ColumnName("labels")] + public VBuffer Labels; + + [ColumnName("scores")] + public VBuffer Scores; + + [ColumnName("boxes")] + public VBuffer Boxes; + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } + + + public class ObjectDetectionLabelMappingOutput + { + [ColumnName("boxes")] + public float[] Boxes; + + [ColumnName("PredictedLabels")] + public string[] Labels; + + [ColumnName("scores")] + public float[] Scores; + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + return boundingBoxes.ToArray(); + } + } + + public BoundingBox[] GetBoundingBoxes() + { + return this.BoundingBoxes; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var box in this.BoundingBoxes) + { + sb.AppendLine(box.ToString()); + } + + return sb.ToString(); + } + } +} From 8d47c4800227ee3e670f142533d403f4a7441732 Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 9 Jun 2020 14:26:20 -0700 Subject: [PATCH 11/28] small changes to finilize codegen --- .../Templates/Console/ConsumeModel.cs | 2 +- .../Templates/Console/ConsumeModel.tt | 2 +- ...GeneratorTest.ConsumeModel.cs.approved.txt | 2 +- ...ObjectDetectionOnnxMapping.cs.approved.txt | 104 ++++++++++++++++++ ...ObjectDetectionOnnxMapping.cs.received.txt | 104 ++++++++++++++++++ 5 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs index 79b4f8eec1..a6f4845a48 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs @@ -72,7 +72,7 @@ public static PredictionEngine CreatePredictionEngine() if(HasObjectDetectionLabelMapping){ this.Write(" \r\n\t\t\t// Register ObjectDetectionMapping to map predicted labels to correct strin" + "g.\r\n mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetecti" + - "onOnnxMapping).Assembly);\r\n"); + "onLabelMapping).Assembly);\r\n"); } this.Write("\r\n // Load model & create prediction engine\r\n string modelP" + "ath = @\""); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt index 2b3b149adf..be1f822afc 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt @@ -48,7 +48,7 @@ namespace <#= Namespace #>.Model <#} #> <#if(HasObjectDetectionLabelMapping){ #> // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionOnnxMapping).Assembly); + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); <#} #> // Load model & create prediction engine diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt index 56cf9d9a2d..dbd179ea16 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt @@ -30,7 +30,7 @@ namespace CodeGenTest.Model mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionOnnxMapping).Assembly); + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); // Load model & create prediction engine string modelPath = @"/path/to/model"; diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt new file mode 100644 index 0000000000..4208123483 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt @@ -0,0 +1,104 @@ + // This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace CodeGenTest.Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[] { "label1", "label2", "label3", }; + + public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + { + long[] oldLabels = input.Labels.GetValues().ToArray(); + output.Labels = new string[oldLabels.Length]; + output.Scores = input.Scores.GetValues().ToArray(); + output.Boxes = input.Boxes.GetValues().ToArray(); + for (int i = 0; i < oldLabels.Length; i++) + { + output.Labels[i] = Label[oldLabels[i]]; + } + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class ObjectDetectionLabelMappingInput + { + [ColumnName("labels")] + public VBuffer Labels; + + [ColumnName("scores")] + public VBuffer Scores; + + [ColumnName("boxes")] + public VBuffer Boxes; + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } + + + public class ObjectDetectionLabelMappingOutput + { + [ColumnName("boxes")] + public float[] Boxes; + + [ColumnName("PredictedLabels")] + public string[] Labels; + + [ColumnName("scores")] + public float[] Scores; + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + return boundingBoxes.ToArray(); + } + } + + public BoundingBox[] GetBoundingBoxes() + { + return this.BoundingBoxes; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var box in this.BoundingBoxes) + { + sb.AppendLine(box.ToString()); + } + + return sb.ToString(); + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt new file mode 100644 index 0000000000..862aa53ab8 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt @@ -0,0 +1,104 @@ +// This file was auto-generated by ML.NET Model Builder. + +using Microsoft.ML.Data; +using Microsoft.ML.Transforms; +using System; +using System.Linq; + +namespace CodeGenTest.Model +{ + [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] + public class ObjectDetectionLabelMapping : CustomMappingFactory + { + public static string[] Label = new string[] { "label1", "label2", "label3", }; + + public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + { + long[] oldLabels = input.Labels.GetValues().ToArray(); + output.Labels = new string[oldLabels.Length]; + output.Scores = input.Scores.GetValues().ToArray(); + output.Boxes = input.Boxes.GetValues().ToArray(); + for (int i = 0; i < oldLabels.Length; i++) + { + output.Labels[i] = Label[oldLabels[i]]; + } + } + + // This factory method will be called when loading the model to get the mapping operation. + public override Action GetMapping() + { + return Mapping; + } + } + + public class ObjectDetectionLabelMappingInput + { + [ColumnName("labels")] + public VBuffer Labels; + + [ColumnName("scores")] + public VBuffer Scores; + + [ColumnName("boxes")] + public VBuffer Boxes; + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } + + + public class ObjectDetectionLabelMappingOutput + { + [ColumnName("boxes")] + public float[] Boxes; + + [ColumnName("PredictedLabels")] + public string[] Labels; + + [ColumnName("scores")] + public float[] Scores; + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + return boundingBoxes.ToArray(); + } + } + + public BoundingBox[] GetBoundingBoxes() + { + return this.BoundingBoxes; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var box in this.BoundingBoxes) + { + sb.AppendLine(box.ToString()); + } + + return sb.ToString(); + } + } +} From 92263955ac42a2b53e94cb3c0ec1eae3d1d81b7c Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 9 Jun 2020 23:34:43 -0700 Subject: [PATCH 12/28] Made changes based on csproj --- .../AzureAttachConsoleAppCodeGenerator.cs | 2 +- .../CSharp/TransformGenerators.cs | 2 +- .../Azure/Model/ObjectDetectionOnnxMapping.cs | 58 +++++++++---------- .../Azure/Model/ObjectDetectionOnnxMapping.tt | 27 ++------- .../Azure/Model/ReshapeTransformer.cs | 11 ++-- .../Azure/Model/ReshapeTransformer.tt | 2 +- .../Templates/Console/ModelOutputClass.cs | 6 +- .../Templates/Console/ModelOutputClass.tt | 4 ++ .../Templates/Console/PredictProgram.cs | 9 ++- .../Templates/Console/PredictProgram.tt | 5 ++ ...CodeGenTest.ConsoleApp.csproj.approved.txt | 1 + ...GeneratorTest.ModelBuilder.cs.approved.txt | 2 +- ...eGeneratorTest.ModelOutput.cs.approved.txt | 2 + ...bjectDetectionLabelMapping.cs.approved.txt | 27 ++------- ...nCodeGeneratorTest.Program.cs.approved.txt | 3 +- ...torTest.ReshapeTransformer.cs.approved.txt | 2 +- .../ConsoleCodeGeneratorTests.cs | 4 +- 17 files changed, 75 insertions(+), 92 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs index 1f794bd167..506b974fae 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs @@ -68,7 +68,7 @@ public AzureAttachConsoleAppCodeGenerator(Pipeline pipeline, ColumnInferenceResu IncludeMklComponentsPackage = false, IncludeLightGBMPackage = false, IncludeFastTreePackage = false, - IncludeImageTransformerPackage = _settings.IsImage, + IncludeImageTransformerPackage = _settings.IsImage || _settings.IsObjectDetection, IncludeImageClassificationPackage = false, IncludeOnnxPackage = true, IncludeOnnxRuntime = _settings.IsObjectDetection, diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index 17831fd8cc..6d5d527bbb 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -472,7 +472,7 @@ public ApplyObjectDetectionOnnxModel(PipelineNode node) : base(node) { } public override string GenerateTransformer() { - return @"ApplyOnnxModel(modelFile: modelFile, outputColumnNames: new[] { ""boxes"",""labels"", ""scores"" }, inputColumnNames: new[] { ""input"" })"; + return @"ApplyOnnxModel(modelFile: ONNX_MODEL, outputColumnNames: new[] { ""boxes"",""labels"", ""scores"" }, inputColumnNames: new[] { ""input"" })"; } } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs index 99fbea5ff9..8b854cf64d 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs @@ -31,12 +31,13 @@ public virtual string TransformText() MB_Annotation(); } this.Write("\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML.Transforms;\r\nusing System;\r\nusing " + - "System.Linq;\r\n\r\nnamespace "); + "System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace" + + " "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); this.Write(@".Model { [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory + public class ObjectDetectionLabelMapping : CustomMappingFactory { public static string[] Label = new string[]{"); foreach(var label in ImportLabels){ @@ -45,35 +46,30 @@ public class ObjectDetectionLabelMapping : CustomMappingFactory GetMapping()\r\n " + - " {\r\n return Mapping;\r\n }\r\n }\r\n\r\n public class ObjectDe" + - "tectionLabelMappingInput\r\n {\r\n [ColumnName(\"labels\")]\r\n public " + - "VBuffer Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public VBuffer<" + - "float> Scores;\r\n\r\n [ColumnName(\"boxes\")]\r\n public VBuffer B" + - "oxes;\r\n }\r\n\r\n public class BoundingBox\r\n {\r\n public float Top;\r\n" + - "\r\n public float Left;\r\n\r\n public float Right;\r\n\r\n public fl" + - "oat Bottom;\r\n\r\n public string Label;\r\n\r\n public float Score;\r\n\r\n " + - " public override string ToString()\r\n {\r\n return $\"Top: {t" + - "his.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: " + - "{this.Label}, Score: {this.Score}\";\r\n }\r\n }\r\n\r\n\r\n public class Obje" + - "ctDetectionLabelMappingOutput\r\n {\r\n [ColumnName(\"boxes\")]\r\n pub" + - "lic float[] Boxes;\r\n\r\n [ColumnName(\"PredictedLabels\")]\r\n public st" + - "ring[] Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores;" + - "\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n get\r\n " + - " {\r\n var boundingBoxes = new List();\r\n " + - " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n p" + - "ublic BoundingBox[] GetBoundingBoxes()\r\n {\r\n return this.Bound" + - "ingBoxes;\r\n }\r\n\r\n public override string ToString()\r\n {\r\n " + - " var sb = new StringBuilder();\r\n\r\n foreach (var box in this." + - "BoundingBoxes)\r\n {\r\n sb.AppendLine(box.ToString());\r\n " + - " }\r\n\r\n return sb.ToString();\r\n }\r\n }\r\n}\r\n"); + "ObjectDetectionLabelMappingOutput output)\r\n {\r\n long[] oldLabe" + + "ls = input.Labels.GetValues().ToArray();\r\n output.Labels = new string" + + "[oldLabels.Length];\r\n output.Scores = input.Scores.GetValues().ToArra" + + "y();\r\n output.Boxes = input.Boxes.GetValues().ToArray();\r\n " + + " for (int i = 0; i < oldLabels.Length; i++)\r\n {\r\n outp" + + "ut.Labels[i] = Label[oldLabels[i]];\r\n }\r\n }\r\n\r\n // This" + + " factory method will be called when loading the model to get the mapping operati" + + "on.\r\n public override Action GetMapping()\r\n {\r\n return Mapping;\r\n " + + " }\r\n }\r\n\r\n public class ObjectDetectionLabelMappingInput\r\n {\r\n " + + " [ColumnName(\"labels\")]\r\n public VBuffer Labels;\r\n\r\n [Co" + + "lumnName(\"scores\")]\r\n public VBuffer Scores;\r\n\r\n [ColumnNam" + + "e(\"boxes\")]\r\n public VBuffer Boxes;\r\n }\r\n\r\n\r\n\r\n public class" + + " ObjectDetectionLabelMappingOutput\r\n {\r\n [ColumnName(\"boxes\")]\r\n " + + " public float[] Boxes;\r\n\r\n [ColumnName(\"PredictedLabels\")]\r\n publ" + + "ic string[] Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Sc" + + "ores;\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n get" + + "\r\n {\r\n var boundingBoxes = new List();\r\n " + + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n " + + " public BoundingBox[] GetBoundingBoxes()\r\n {\r\n return this." + + "BoundingBoxes;\r\n }\r\n\r\n public override string ToString()\r\n " + + "{\r\n var sb = new StringBuilder();\r\n\r\n foreach (var box in " + + "this.BoundingBoxes)\r\n {\r\n sb.AppendLine(box.ToString()" + + ");\r\n }\r\n\r\n return sb.ToString();\r\n }\r\n }\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt index 7a4aa14fc2..223c4b1ddd 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt @@ -12,16 +12,18 @@ using Microsoft.ML.Data; using Microsoft.ML.Transforms; using System; +using System.Collections.Generic; using System.Linq; +using System.Text; namespace <#= Namespace #>.Model { [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory + public class ObjectDetectionLabelMapping : CustomMappingFactory { public static string[] Label = new string[]{<#foreach(var label in ImportLabels){#>"<#=label#>",<#}#>}; - public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + public static void Mapping(ObjectDetectionLabelMappingInput input, ObjectDetectionLabelMappingOutput output) { long[] oldLabels = input.Labels.GetValues().ToArray(); output.Labels = new string[oldLabels.Length]; @@ -34,7 +36,7 @@ namespace <#= Namespace #>.Model } // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() + public override Action GetMapping() { return Mapping; } @@ -52,25 +54,6 @@ namespace <#= Namespace #>.Model public VBuffer Boxes; } - public class BoundingBox - { - public float Top; - - public float Left; - - public float Right; - - public float Bottom; - - public string Label; - - public float Score; - - public override string ToString() - { - return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; - } - } public class ObjectDetectionLabelMappingOutput diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs index 7b6506ba24..abcbc5ceb6 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs @@ -30,13 +30,10 @@ public virtual string TransformText() } else if(Target == CSharp.GenerateTarget.ModelBuilder){ MB_Annotation(); } - this.Write(@" -using System; -using System.Linq; -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; - -namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers + this.Write("\r\nusing System;\r\nusing System.Linq;\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML" + + ".Transforms;\r\n\r\nnamespace "); + this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); + this.Write(@".Model { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] public class ReshapeTransformer : CustomMappingFactory diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt index d69e9a38b7..e7f352ba3e 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt @@ -14,7 +14,7 @@ using System.Linq; using Microsoft.ML.Data; using Microsoft.ML.Transforms; -namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers +namespace <#= Namespace #>.Model { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] public class ReshapeTransformer : CustomMappingFactory diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs index 2332f21c00..d5f3f3086c 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs @@ -30,7 +30,11 @@ public virtual string TransformText() } else if(Target == CSharp.GenerateTarget.ModelBuilder){ MB_Annotation(); } - this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\n\r\nnamespace "); + this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\n"); +if("ObjectDetection".Equals(TaskType)){ + this.Write("using System.Collections.Generic;\r\nusing System.Linq;\r\n"); + } + this.Write("\r\nnamespace "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n"); if("BinaryClassification".Equals(TaskType)){ diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt index eb45998467..7b5a39bb56 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt @@ -11,6 +11,10 @@ using System; using Microsoft.ML.Data; +<#if("ObjectDetection".Equals(TaskType)){ #> +using System.Collections.Generic; +using System.Linq; +<# }#> namespace <#= Namespace #>.Model { diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs index 439e47c191..157b40a228 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs @@ -53,12 +53,14 @@ public virtual string TransformText() this.Write(" ModelInput sampleData = new ModelInput();\r\n"); } this.Write("\r\n\t\t\t// Make a single prediction on the sample data and print results\r\n\t\t\tvar pre" + - "dictionResult = ConsumeModel.Predict(sampleData);\r\n\r\n\t\t\tConsole.WriteLine(\"Using" + - " model to make single prediction -- Comparing actual "); + "dictionResult = ConsumeModel.Predict(sampleData);\r\n\r\n "); +if(!"ObjectDetection".Equals(TaskType) ){ + this.Write("\t\t\tConsole.WriteLine(\"Using model to make single prediction -- Comparing actual "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" with predicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" from sample data...\\n\\n\");\r\n"); +} if(SampleData != null) { foreach(var kv in SampleData){ this.Write("\t\t\tConsole.WriteLine($\""); @@ -82,6 +84,9 @@ public virtual string TransformText() this.Write(" value {predictionResult.Prediction} \\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" scores: [{String.Join(\",\", predictionResult.Score)}]\\n\\n\");\r\n"); +} else if("ObjectDetection".Equals(TaskType)){ + this.Write("\t\t\tConsole.WriteLine(\"\\n\\nPredicted Boxes:\\n\");\r\n Console.WriteLine(pr" + + "edictionResult);\r\n"); } this.Write(" Console.WriteLine(\"=============== End of process, hit any key to fin" + "ish ===============\");\r\n Console.ReadKey();\r\n }\r\n }\r\n}\r\n"); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt index 4035d2ebaf..2d34595dc4 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt @@ -35,7 +35,9 @@ namespace <#= Namespace #>.ConsoleApp // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); + <#if(!"ObjectDetection".Equals(TaskType) ){ #> Console.WriteLine("Using model to make single prediction -- Comparing actual <#= Utils.Normalize(LabelName) #> with predicted <#= Utils.Normalize(LabelName) #> from sample data...\n\n"); +<#} #> <# if(SampleData != null) {#> <#foreach(var kv in SampleData){#> Console.WriteLine($"<#= kv.Key #>: {sampleData.<#= kv.Key #>}"); @@ -47,6 +49,9 @@ namespace <#= Namespace #>.ConsoleApp Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Score}\n\n"); <#} else if("MulticlassClassification".Equals(TaskType)){#> Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #> value {predictionResult.Prediction} \nPredicted <#= Utils.Normalize(LabelName) #> scores: [{String.Join(",", predictionResult.Score)}]\n\n"); +<#} else if("ObjectDetection".Equals(TaskType)){#> + Console.WriteLine("\n\nPredicted Boxes:\n"); + Console.WriteLine(predictionResult); <#} #> Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index 86413329df..a9eaa5927a 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -6,6 +6,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 9627604a25..526e3821fa 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -51,7 +51,7 @@ namespace CodeGenTest.ConsoleApp .Append(mlContext.Transforms.CustomMapping( (input, output) => ReshapeTransformer.Mapping(input, output), contractName: nameof(ReshapeTransformer))) - .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: modelFile, outputColumnNames: new[] { "boxes", "labels", "scores" }, inputColumnNames: new[] { "input" })) + .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL, outputColumnNames: new[] { "boxes", "labels", "scores" }, inputColumnNames: new[] { "input" })) .Append(mlContext.Transforms.CustomMapping( // added this line not sure what to do. (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), contractName: nameof(ObjectDetectionLabelMapping))); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt index 8f6db0f9d3..932ac45631 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -2,6 +2,8 @@ using System; using Microsoft.ML.Data; +using System.Collections.Generic; +using System.Linq; namespace CodeGenTest.Model { diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt index 862aa53ab8..1f56207e5f 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt @@ -3,16 +3,18 @@ using Microsoft.ML.Data; using Microsoft.ML.Transforms; using System; +using System.Collections.Generic; using System.Linq; +using System.Text; namespace CodeGenTest.Model { [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory + public class ObjectDetectionLabelMapping : CustomMappingFactory { public static string[] Label = new string[] { "label1", "label2", "label3", }; - public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) + public static void Mapping(ObjectDetectionLabelMappingInput input, ObjectDetectionLabelMappingOutput output) { long[] oldLabels = input.Labels.GetValues().ToArray(); output.Labels = new string[oldLabels.Length]; @@ -25,7 +27,7 @@ namespace CodeGenTest.Model } // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() + public override Action GetMapping() { return Mapping; } @@ -43,25 +45,6 @@ namespace CodeGenTest.Model public VBuffer Boxes; } - public class BoundingBox - { - public float Top; - - public float Left; - - public float Right; - - public float Bottom; - - public string Label; - - public float Score; - - public override string ToString() - { - return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; - } - } public class ObjectDetectionLabelMappingOutput diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt index 43d3fe51fc..97c0edc623 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.Program.cs.approved.txt @@ -15,7 +15,8 @@ namespace CodeGenTest.ConsoleApp // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); - Console.WriteLine("Using model to make single prediction -- Comparing actual Label with predicted Label from sample data...\n\n"); + Console.WriteLine("\n\nPredicted Boxes:\n"); + Console.WriteLine(predictionResult); Console.WriteLine("=============== End of process, hit any key to finish ==============="); Console.ReadKey(); } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt index e7a97589f2..d1b48b85b9 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt @@ -5,7 +5,7 @@ using System.Linq; using Microsoft.ML.Data; using Microsoft.ML.Transforms; -namespace Microsoft.ML.ModelBuilder.AutoMLService.RemoteAutoML.ObjectDetectionTransformerHelpers +namespace CodeGenTest.Model { [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] public class ReshapeTransformer : CustomMappingFactory diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 1396e77745..c5820215fa 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -219,7 +219,7 @@ public void AzureImageCodeGeneratorTest() var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); foreach (var project in codeGen.ToSolution()) { - foreach(var projectFile in project) + foreach (var projectFile in project) { NamerFactory.AdditionalInformation = projectFile.Name; Approvals.Verify(((ICSharpFile)projectFile).File); @@ -253,10 +253,12 @@ public void AzureObjectDetectionCodeGeneratorTest() ClassificationLabel = new string[] { "label1", "label2", "label3" }, }; var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); + //codeGen.GenerateOutput(); // use this to see output in project. foreach (var project in codeGen.ToSolution()) { foreach (var projectFile in project) { + NamerFactory.AdditionalInformation = projectFile.Name; Approvals.Verify(((ICSharpFile)projectFile).File); } From b399a276e20aee54b689a8a83ab2647895f0d649 Mon Sep 17 00:00:00 2001 From: Tevin Date: Wed, 10 Jun 2020 08:24:45 -0700 Subject: [PATCH 13/28] minor changes to make final build --- .../TemplateTest.TestPredictProgram_WithSampleData.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt index a0ae7f5bef..2a64562680 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestPredictProgram_WithSampleData.approved.txt @@ -20,7 +20,7 @@ namespace Namespace.ConsoleApp // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); - Console.WriteLine("Using model to make single prediction -- Comparing actual LabelName with predicted LabelName from sample data...\n\n"); + Console.WriteLine("Using model to make single prediction -- Comparing actual LabelName with predicted LabelName from sample data...\n\n"); Console.WriteLine($"key1: {sampleData.key1}"); Console.WriteLine($"key2: {sampleData.key2}"); Console.WriteLine($"key3: {sampleData.key3}"); From ce357404d104b7c5855c7f6682167dab99779c24 Mon Sep 17 00:00:00 2001 From: Tevin Date: Wed, 10 Jun 2020 11:51:00 -0700 Subject: [PATCH 14/28] updated onnxruntime to 1.3 --- .../Templates/Console/ModelProject.cs | 2 +- .../Templates/Console/ModelProject.tt | 2 +- .../Templates/Console/PredictProject.cs | 2 +- .../Templates/Console/PredictProject.tt | 2 +- ...CodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt | 2 +- ...ctionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs index 11a15048df..493d94d192 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs @@ -56,7 +56,7 @@ public virtual string TransformText() this.Write("\" />\r\n"); } if (IncludeOnnxRuntime){ - this.Write(" \r\n"); + this.Write(" \r\n"); } if (IncludeImageClassificationPackage){ this.Write(" <#}#> <# if (IncludeOnnxRuntime){ #> - + <#}#> <# if (IncludeImageClassificationPackage){ #> diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs index e2167dbbec..b60340fd05 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs @@ -57,7 +57,7 @@ public virtual string TransformText() this.Write("\" />\r\n"); } if (IncludeOnnxRuntime){ - this.Write(" \r\n"); + this.Write(" \r\n"); } if (IncludeResNet18Package){ this.Write(" <#}#> <# if (IncludeOnnxRuntime){ #> - + <#}#> <# if (IncludeResNet18Package){ #> diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index a9eaa5927a..7e87762f76 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -8,7 +8,7 @@ - + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt index b5f9ed0d42..b51befbcf6 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -6,7 +6,7 @@ - + From 7ec0c2b9db9f8afb4e45fcf250698ee24fd1ee7f Mon Sep 17 00:00:00 2001 From: Tevin Date: Wed, 10 Jun 2020 14:47:25 -0700 Subject: [PATCH 15/28] targetting older automl --- .../Microsoft.ML.CodeGenerator.nupkgproj | 2 +- .../Microsoft.ML.CodeGenerator.csproj | 2 +- .../Templates/Console/ModelProject.cs | 1 - .../Templates/Console/ModelProject.tt | 1 - .../Templates/Console/PredictProject.cs | 1 - .../Templates/Console/PredictProject.tt | 1 - ...CodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt | 1 - ...ctionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt | 1 - 8 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj b/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj index 48c288fb6b..6712b551c3 100644 --- a/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj +++ b/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj @@ -6,7 +6,7 @@ - + diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index 995766426b..d205e95058 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs index 493d94d192..deec534e85 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs @@ -56,7 +56,6 @@ public virtual string TransformText() this.Write("\" />\r\n"); } if (IncludeOnnxRuntime){ - this.Write(" \r\n"); } if (IncludeImageClassificationPackage){ this.Write(" <#}#> <# if (IncludeOnnxRuntime){ #> - <#}#> <# if (IncludeImageClassificationPackage){ #> diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs index b60340fd05..f1c4bfdf9a 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs @@ -57,7 +57,6 @@ public virtual string TransformText() this.Write("\" />\r\n"); } if (IncludeOnnxRuntime){ - this.Write(" \r\n"); } if (IncludeResNet18Package){ this.Write(" <#}#> <# if (IncludeOnnxRuntime){ #> - <#}#> <# if (IncludeResNet18Package){ #> diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index 7e87762f76..5a0f249e82 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -8,7 +8,6 @@ - diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt index b51befbcf6..d2622aecf7 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -6,7 +6,6 @@ - From 0fe683ea45183842b45eee5353a4a5d3f388ed0e Mon Sep 17 00:00:00 2001 From: Tevin Date: Thu, 11 Jun 2020 10:41:03 -0700 Subject: [PATCH 16/28] taking out dependency on automl taskkind for OD --- .../AzureAttachConsoleAppCodeGenerator.cs | 1 + .../AzureCodeGenerator/AzureAttachModelCodeGenerator.cs | 1 + .../Templates/Console/ModelOutputClass.cs | 7 ++++--- .../Templates/Console/ModelOutputClass.tt | 7 ++++--- .../Templates/Console/PredictProgram.cs | 7 ++++--- .../Templates/Console/PredictProgram.tt | 7 ++++--- .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 8 +++++++- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs index 506b974fae..9b96a84d7e 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachConsoleAppCodeGenerator.cs @@ -94,6 +94,7 @@ public AzureAttachConsoleAppCodeGenerator(Pipeline pipeline, ColumnInferenceResu Separator = _columnInferenceResult.TextLoaderOptions.Separators.FirstOrDefault(), Target = _settings.Target, SampleData = sampleResult, + IsObjectDetection = _settings.IsObjectDetection, }.TransformText(), Name = "Program.cs", }; diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index d3f90d696e..682cda0347 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -66,6 +66,7 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c Target = _settings.Target, TaskType = _settings.MlTask.ToString(), PredictionLabelType = labelTypeCsharp.Name, + IsObjectDetection = _settings.IsObjectDetection, }.TransformText(), Name = "ModelOutput.cs", }; diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs index d5f3f3086c..510157409e 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs @@ -31,7 +31,7 @@ public virtual string TransformText() MB_Annotation(); } this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\n"); -if("ObjectDetection".Equals(TaskType)){ +if(IsObjectDetection){ this.Write("using System.Collections.Generic;\r\nusing System.Linq;\r\n"); } this.Write("\r\nnamespace "); @@ -41,14 +41,14 @@ public virtual string TransformText() this.Write(" // ColumnName attribute is used to change the column name from\r\n /" + "/ its default value, which is the name of the field.\r\n [ColumnName(\"Predi" + "ctedLabel\")]\r\n public bool Prediction { get; set; }\r\n"); - } if("MulticlassClassification".Equals(TaskType)){ + } if("MulticlassClassification".Equals(TaskType) && !IsObjectDetection){ this.Write(" // ColumnName attribute is used to change the column name from\r\n /" + "/ its default value, which is the name of the field.\r\n [ColumnName(\"Predi" + "ctedLabel\")]\r\n public "); this.Write(this.ToStringHelper.ToStringWithCulture(PredictionLabelType)); this.Write(" Prediction { get; set; }\r\n"); } -if("ObjectDetection".Equals(TaskType)){ +if(IsObjectDetection){ this.Write(" [ColumnName(\"boxes\")]\r\n public float[] Boxes { get; set; }\r\n\r\n " + " [ColumnName(\"PredictedLabels\")]\r\n public string[] Labels { get; set; " + "}\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; set; }" + @@ -84,6 +84,7 @@ public virtual string TransformText() public string TaskType {get;set;} public string PredictionLabelType {get;set;} public string Namespace {get;set;} +public bool IsObjectDetection {get;set;} internal CSharp.GenerateTarget Target {get;set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt index 7b5a39bb56..61ea6fddd4 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt @@ -11,7 +11,7 @@ using System; using Microsoft.ML.Data; -<#if("ObjectDetection".Equals(TaskType)){ #> +<#if(IsObjectDetection){ #> using System.Collections.Generic; using System.Linq; <# }#> @@ -25,13 +25,13 @@ namespace <#= Namespace #>.Model // its default value, which is the name of the field. [ColumnName("PredictedLabel")] public bool Prediction { get; set; } -<# } if("MulticlassClassification".Equals(TaskType)){ #> +<# } if("MulticlassClassification".Equals(TaskType) && !IsObjectDetection){ #> // ColumnName attribute is used to change the column name from // its default value, which is the name of the field. [ColumnName("PredictedLabel")] public <#= PredictionLabelType#> Prediction { get; set; } <# }#> -<#if("ObjectDetection".Equals(TaskType)){ #> +<#if(IsObjectDetection){ #> [ColumnName("boxes")] public float[] Boxes { get; set; } @@ -98,5 +98,6 @@ namespace <#= Namespace #>.Model public string TaskType {get;set;} public string PredictionLabelType {get;set;} public string Namespace {get;set;} +public bool IsObjectDetection {get;set;} internal CSharp.GenerateTarget Target {get;set;} #> diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs index 157b40a228..782c0f1fb1 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.cs @@ -54,7 +54,7 @@ public virtual string TransformText() } this.Write("\r\n\t\t\t// Make a single prediction on the sample data and print results\r\n\t\t\tvar pre" + "dictionResult = ConsumeModel.Predict(sampleData);\r\n\r\n "); -if(!"ObjectDetection".Equals(TaskType) ){ +if(!IsObjectDetection){ this.Write("\t\t\tConsole.WriteLine(\"Using model to make single prediction -- Comparing actual "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" with predicted "); @@ -78,13 +78,13 @@ public virtual string TransformText() this.Write("\t\t\tConsole.WriteLine($\"\\n\\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(": {predictionResult.Score}\\n\\n\");\r\n"); -} else if("MulticlassClassification".Equals(TaskType)){ +} else if("MulticlassClassification".Equals(TaskType) && !IsObjectDetection){ this.Write("\t\t\tConsole.WriteLine($\"\\n\\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" value {predictionResult.Prediction} \\nPredicted "); this.Write(this.ToStringHelper.ToStringWithCulture(Utils.Normalize(LabelName))); this.Write(" scores: [{String.Join(\",\", predictionResult.Score)}]\\n\\n\");\r\n"); -} else if("ObjectDetection".Equals(TaskType)){ +} else if(IsObjectDetection){ this.Write("\t\t\tConsole.WriteLine(\"\\n\\nPredicted Boxes:\\n\");\r\n Console.WriteLine(pr" + "edictionResult);\r\n"); } @@ -100,6 +100,7 @@ public virtual string TransformText() public bool AllowQuoting {get;set;} public bool AllowSparse {get;set;} public bool HasHeader {get;set;} +public bool IsObjectDetection {get; set;} public IList Features {get;set;} internal CSharp.GenerateTarget Target {get;set;} public IDictionary SampleData {get;set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt index 2d34595dc4..fe00c8489a 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProgram.tt @@ -35,7 +35,7 @@ namespace <#= Namespace #>.ConsoleApp // Make a single prediction on the sample data and print results var predictionResult = ConsumeModel.Predict(sampleData); - <#if(!"ObjectDetection".Equals(TaskType) ){ #> + <#if(!IsObjectDetection){ #> Console.WriteLine("Using model to make single prediction -- Comparing actual <#= Utils.Normalize(LabelName) #> with predicted <#= Utils.Normalize(LabelName) #> from sample data...\n\n"); <#} #> <# if(SampleData != null) {#> @@ -47,9 +47,9 @@ namespace <#= Namespace #>.ConsoleApp Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Prediction}\n\n"); <#} else if("Regression".Equals(TaskType) || "Recommendation".Equals(TaskType)){#> Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #>: {predictionResult.Score}\n\n"); -<#} else if("MulticlassClassification".Equals(TaskType)){#> +<#} else if("MulticlassClassification".Equals(TaskType) && !IsObjectDetection){#> Console.WriteLine($"\n\nPredicted <#= Utils.Normalize(LabelName) #> value {predictionResult.Prediction} \nPredicted <#= Utils.Normalize(LabelName) #> scores: [{String.Join(",", predictionResult.Score)}]\n\n"); -<#} else if("ObjectDetection".Equals(TaskType)){#> +<#} else if(IsObjectDetection){#> Console.WriteLine("\n\nPredicted Boxes:\n"); Console.WriteLine(predictionResult); <#} #> @@ -66,6 +66,7 @@ public char Separator {get;set;} public bool AllowQuoting {get;set;} public bool AllowSparse {get;set;} public bool HasHeader {get;set;} +public bool IsObjectDetection {get; set;} public IList Features {get;set;} internal CSharp.GenerateTarget Target {get;set;} public IDictionary SampleData {get;set;} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index c5820215fa..ee6c3eb518 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -237,15 +237,21 @@ public void AzureObjectDetectionCodeGeneratorTest() (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(@"/path/to/onnxModel"); var setting = new CodeGeneratorSettings() { + //TrainDataset = @"C:\Users\Tevin\AppData\Local\Temp\2a11921f-1201-4a85-ae31-f69b7e58ebd7.csv", + //ModelPath = @"C:\Users\Tevin\source\repos\TESTOD\TESTODML.Model\MLModel.zip", TrainDataset = @"/path/to/dataset", ModelPath = @"/path/to/model", - MlTask = TaskKind.ObjectDetection, + MlTask = TaskKind.MulticlassClassification, OutputName = @"CodeGenTest", OutputBaseDir = @"/path/to/codegen", + // OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", LabelName = "Label", Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", + //StablePackageVersion = "1.5.0-preview2", UnstablePackageVersion = "unstableversion", + //UnstablePackageVersion = "0.17.0-preview2", + //OnnxModelPath = @"C:\Users\Tevin\Desktop\bestModel.onnx", OnnxModelPath = @"/path/to/onnxModel", IsAzureAttach = true, IsImage = false, From b7556da4c5deaae831b788df0cedfc41f1a6fe2d Mon Sep 17 00:00:00 2001 From: Tevin Date: Thu, 11 Jun 2020 12:07:48 -0700 Subject: [PATCH 17/28] final build got predictions working for OD --- .../Templates/Console/ModelOutputClass.cs | 16 +++++++++------- .../Templates/Console/ModelOutputClass.tt | 5 +++++ ...CodeGeneratorTest.ModelOutput.cs.approved.txt | 5 +++++ .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 8 ++++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs index 510157409e..3c08480062 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.cs @@ -65,13 +65,15 @@ public virtual string TransformText() "oxes[(index * 4) + 3],\r\n Score = scores[index]," + "\r\n Label = labels[index].ToString(),\r\n " + " };\r\n }).ToList();\r\n " + - " return boundingBoxes.ToArray();\r\n }\r\n }\r\n }\r\n\r\n public " + - "class BoundingBox\r\n {\r\n public float Top;\r\n\r\n public float Left" + - ";\r\n\r\n public float Right;\r\n\r\n public float Bottom;\r\n\r\n publ" + - "ic string Label;\r\n\r\n public float Score;\r\n\r\n public override strin" + - "g ToString()\r\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}" + - ", Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this." + - "Score}\";\r\n }\r\n"); + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n public ove" + + "rride string ToString()\r\n {\r\n return string.Join(\"\\n\", Boundin" + + "gBoxes.Select(x => x.ToString()));\r\n }\r\n }\r\n\r\n public class Boundin" + + "gBox\r\n {\r\n public float Top;\r\n\r\n public float Left;\r\n\r\n " + + "public float Right;\r\n\r\n public float Bottom;\r\n\r\n public string Lab" + + "el;\r\n\r\n public float Score;\r\n\r\n public override string ToString()\r" + + "\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}, Right: {thi" + + "s.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}\";\r\n " + + " }\r\n"); } else if("MulticlassClassification".Equals(TaskType)){ this.Write(" public float[] Score { get; set; }\r\n"); }else{ diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt index 61ea6fddd4..39efa56b6d 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelOutputClass.tt @@ -67,6 +67,11 @@ namespace <#= Namespace #>.Model return boundingBoxes.ToArray(); } } + + public override string ToString() + { + return string.Join("\n", BoundingBoxes.Select(x => x.ToString())); + } } public class BoundingBox diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt index 932ac45631..b11d30b740 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -44,6 +44,11 @@ namespace CodeGenTest.Model return boundingBoxes.ToArray(); } } + + public override string ToString() + { + return string.Join("\n", BoundingBoxes.Select(x => x.ToString())); + } } public class BoundingBox diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index ee6c3eb518..2dbfcb7d26 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -238,19 +238,19 @@ public void AzureObjectDetectionCodeGeneratorTest() var setting = new CodeGeneratorSettings() { //TrainDataset = @"C:\Users\Tevin\AppData\Local\Temp\2a11921f-1201-4a85-ae31-f69b7e58ebd7.csv", - //ModelPath = @"C:\Users\Tevin\source\repos\TESTOD\TESTODML.Model\MLModel.zip", + //ModelPath = @"C:\Users\Tevin\source\repos\ConsoleApp2\ConsoleApp2ML.Model\MLModel.zip", TrainDataset = @"/path/to/dataset", ModelPath = @"/path/to/model", MlTask = TaskKind.MulticlassClassification, OutputName = @"CodeGenTest", OutputBaseDir = @"/path/to/codegen", - // OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", + //OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", LabelName = "Label", Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", - //StablePackageVersion = "1.5.0-preview2", + //StablePackageVersion = "1.5.0-preview", UnstablePackageVersion = "unstableversion", - //UnstablePackageVersion = "0.17.0-preview2", + //UnstablePackageVersion = "0.17.0-preview", //OnnxModelPath = @"C:\Users\Tevin\Desktop\bestModel.onnx", OnnxModelPath = @"/path/to/onnxModel", IsAzureAttach = true, From f1aa8acfc8a5921cd1a67c0d72d4f7aba576a1ad Mon Sep 17 00:00:00 2001 From: Tevin Date: Fri, 12 Jun 2020 14:59:28 -0700 Subject: [PATCH 18/28] took out old test paths to generalize tests --- .../ApprovalTests/ConsoleCodeGeneratorTests.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 2dbfcb7d26..84fba8c37c 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -237,21 +237,15 @@ public void AzureObjectDetectionCodeGeneratorTest() (var pipeline, var columnInference) = GetMockedAzureObjectDetectionPipelineAndInference(@"/path/to/onnxModel"); var setting = new CodeGeneratorSettings() { - //TrainDataset = @"C:\Users\Tevin\AppData\Local\Temp\2a11921f-1201-4a85-ae31-f69b7e58ebd7.csv", - //ModelPath = @"C:\Users\Tevin\source\repos\ConsoleApp2\ConsoleApp2ML.Model\MLModel.zip", TrainDataset = @"/path/to/dataset", ModelPath = @"/path/to/model", MlTask = TaskKind.MulticlassClassification, OutputName = @"CodeGenTest", OutputBaseDir = @"/path/to/codegen", - //OutputBaseDir = @"C:\Users\Tevin\Desktop\Model", LabelName = "Label", Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", - //StablePackageVersion = "1.5.0-preview", UnstablePackageVersion = "unstableversion", - //UnstablePackageVersion = "0.17.0-preview", - //OnnxModelPath = @"C:\Users\Tevin\Desktop\bestModel.onnx", OnnxModelPath = @"/path/to/onnxModel", IsAzureAttach = true, IsImage = false, From 93eaf54cf7ba069a88c11fa8abc734fe4e39b1cc Mon Sep 17 00:00:00 2001 From: Tevin Date: Mon, 15 Jun 2020 09:56:12 -0700 Subject: [PATCH 19/28] cleaning up outdated comments --- .../CodeGenerator/CSharp/TransformGenerators.cs | 2 +- ...bjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index 6d5d527bbb..181248f54d 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -444,7 +444,7 @@ public CustomObjectDetectionLabelMapping(PipelineNode node) : base(node) { } public override string GenerateTransformer() { - return @"CustomMapping( // added this line not sure what to do. + return @"CustomMapping( (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), contractName: nameof(ObjectDetectionLabelMapping))"; } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 526e3821fa..8a6da5814b 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -52,7 +52,7 @@ namespace CodeGenTest.ConsoleApp (input, output) => ReshapeTransformer.Mapping(input, output), contractName: nameof(ReshapeTransformer))) .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL, outputColumnNames: new[] { "boxes", "labels", "scores" }, inputColumnNames: new[] { "input" })) - .Append(mlContext.Transforms.CustomMapping( // added this line not sure what to do. + .Append(mlContext.Transforms.CustomMapping( (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), contractName: nameof(ObjectDetectionLabelMapping))); return pipeline; From 2c03cf886af8d8283cfbefd01a49098c5ada6a77 Mon Sep 17 00:00:00 2001 From: Tevin Date: Mon, 15 Jun 2020 14:06:23 -0700 Subject: [PATCH 20/28] for the build packaging --- .../CodeGenerator/CSharp/TransformGeneratorFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs index 6f00f1e9da..b0320befaa 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs @@ -83,7 +83,7 @@ internal static ITransformGenerator GetInstance(PipelineNode node) } } - // For AzureAttach + // For the AzureAttach if (Enum.TryParse(node.Name, out SpecialTransformer transformer)) { switch (transformer) From 9f784f46df131697231d8994c46c4b6b16fdd92e Mon Sep 17 00:00:00 2001 From: Tevin Date: Tue, 16 Jun 2020 10:07:27 -0700 Subject: [PATCH 21/28] rebuild --- .../CodeGenerator/CSharp/TransformGeneratorFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs index b0320befaa..df47aa52c1 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs @@ -22,7 +22,7 @@ internal enum SpecialTransformer ExtractPixel = 2, NormalizeMapping = 3, LabelMapping = 4, - ObjectDetectionLabelMapping = 5, // Tevin: added for OD mappings + ObjectDetectionLabelMapping = 5, ReshapeTransformer = 6, ObjectDetectionResizeImage = 7, ObjectDetectionExtractPixel = 8, From dad6672876cc3230576522bd3b06da31a483b713 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Tue, 21 Jul 2020 15:22:48 -0700 Subject: [PATCH 22/28] fix tests --- .../AzureAttachModelCodeGenerator.cs | 55 +++++------- .../CSharp/CodeGeneratorSettings.cs | 3 + .../Microsoft.ML.CodeGenerator.csproj | 26 +++--- ...> AzureObjectDetectionModelOutputClass.cs} | 62 ++++++------- .../AzureObjectDetectionModelOutputClass.tt | 89 +++++++++++++++++++ .../Azure/Model/OnnxModelOutputClass.tt | 52 ----------- .../Templates/Console/ConsumeModel.cs | 22 +++-- .../Templates/Console/ConsumeModel.tt | 12 ++- .../Templates/Console/ModelProject.cs | 3 +- .../Templates/Console/ModelProject.tt | 3 - ...neratorTest.test.Model.csproj.approved.txt | 3 - ...Test.CodeGenTest.Model.csproj.approved.txt | 3 - ...CodeGenTest.ConsoleApp.csproj.approved.txt | 1 + ...Test.CodeGenTest.Model.csproj.approved.txt | 1 + ...GeneratorTest.ConsumeModel.cs.approved.txt | 8 +- ...GeneratorTest.ModelBuilder.cs.approved.txt | 4 +- ...eGeneratorTest.ModelOutput.cs.approved.txt | 2 + .../ConsoleCodeGeneratorTests.cs | 10 ++- ...ionModelOutputClass_WithLabel.approved.txt | 79 ++++++++++++++++ ...umeModel_AzureObjectDetection.approved.txt | 47 ++++++++++ ...AzureImage_NotObjectDetection.approved.txt | 39 ++++++++ .../ApprovalTests/TemplateTest.cs | 36 +++++++- 22 files changed, 391 insertions(+), 169 deletions(-) rename src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/{AzureImageModelOutputClass.cs => AzureObjectDetectionModelOutputClass.cs} (80%) create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/OnnxModelOutputClass.tt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt create mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_NotAzureImage_NotObjectDetection.approved.txt diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index 23914a1922..53b9746f2f 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -25,14 +25,18 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator private readonly string _nameSpaceValue; public ICSharpFile ModelInputClass { get; private set; } - public ICSharpFile ModelOutputClass { get; private set; } + public ICSharpFile AzureImageModelOutputClass { get; private set; } + public ICSharpFile AzureObjectDetectionModelOutputClass { get; private set; } public ICSharpFile ModelProject { get; private set; } public ICSharpFile ConsumeModel { get; private set; } public ICSharpFile LabelMapping { get; private set; } + public ICSharpFile ImageLabelMapping { get; private set; } public ICSharpFile ObjectDetectionLabelMapping { get; private set; } public ICSharpFile ReshapeTransformerMap { get; private set; } public ICSharpFile ObjectDetectionConsumeModel { get; private set; } public string Name { get; set; } + + public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults columnInferenceResults, CodeGeneratorSettings options, string namespaceValue) { _pipeline = pipeline; _settings = options; @@ -54,20 +58,28 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator var labelType = _columnInferenceResult.TextLoaderOptions.Columns.Where(t => t.Name == _settings.LabelName).First().DataKind; Type labelTypeCsharp = Utils.GetCSharpType(labelType); - ModelOutputClass = new CSharpCodeFile() + AzureImageModelOutputClass = new CSharpCodeFile() { File = new AzureImageModelOutputClass() { Namespace = _nameSpaceValue, Target = _settings.Target, - TaskType = _settings.MlTask.ToString(), - PredictionLabelType = labelTypeCsharp.Name, - IsObjectDetection = _settings.IsObjectDetection, Labels = _settings.ClassificationLabel, }.TransformText(), Name = "ModelOutput.cs", }; + AzureObjectDetectionModelOutputClass = new CSharpCodeFile() + { + File = new AzureObjectDetectionModelOutputClass() + { + Namespace = _nameSpaceValue, + Target = _settings.Target, + Labels = _settings.ObjectLabel, + }.TransformText(), + Name = "ModelOutput.cs", + }; + ModelProject = new CSharpProjectFile() { File = new ModelProject() @@ -101,17 +113,6 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator Name = "LabelMapping.cs", }; - ImageLabelMapping = new CSharpCodeFile() - { - File = new ImageLabelMapping() - { - Target = _settings.Target, - Namespace = _nameSpaceValue, - Labels = _settings.ClassificationLabel, - }.TransformText(), - Name = "LabelMapping.cs", - }; - // Tevin: added ObjectDetectionLabelMapping = new CSharpCodeFile() { @@ -134,19 +135,6 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator Name = "ReshapeTransformer.cs", }; - ObjectDetectionConsumeModel = new CSharpCodeFile() - { - File = new ConsumeModel() - { - Namespace = _nameSpaceValue, - Target = _settings.Target, - HasObjectDetectionLabelMapping = _settings.IsObjectDetection, - HasReshapeTransformer = _settings.IsObjectDetection, - MLNetModelpath = _settings.ModelPath, - }.TransformText(), - Name = "ConsumeModel.cs" - }; - ConsumeModel = new CSharpCodeFile() { File = new ConsumeModel() @@ -156,6 +144,7 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator MLNetModelName = _settings.ModelName, OnnxModelName = _settings.OnnxModelName, IsAzureImage = _settings.IsAzureAttach && _settings.IsImage, + IsAzureObjectDetection = _settings.IsObjectDetection && _settings.IsAzureAttach, }.TransformText(), Name = "ConsumeModel.cs", }; @@ -169,7 +158,7 @@ public ICSharpProject ToProject() project = new CSharpProject() { ModelInputClass, - ModelOutputClass, + AzureImageModelOutputClass, ConsumeModel, ModelProject, }; @@ -178,8 +167,8 @@ public ICSharpProject ToProject() project = new CSharpProject() { ModelInputClass, - ModelOutputClass, - ObjectDetectionConsumeModel, + AzureObjectDetectionModelOutputClass, + ConsumeModel, ModelProject, ReshapeTransformerMap, ObjectDetectionLabelMapping, @@ -190,7 +179,7 @@ public ICSharpProject ToProject() project = new CSharpProject() { ModelInputClass, - ModelOutputClass, + AzureImageModelOutputClass, ConsumeModel, ModelProject, LabelMapping, diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs index fd4f997ce4..e0fa2fb191 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/CodeGeneratorSettings.cs @@ -17,6 +17,7 @@ public CodeGeneratorSettings() Target = GenerateTarget.Cli; OnnxInputMapping = new Dictionary(); ClassificationLabel = new string[] { }; + ObjectLabel = new string[] { }; } public string LabelName { get; set; } @@ -37,6 +38,8 @@ public CodeGeneratorSettings() /// public string[] ClassificationLabel { get; set; } + public string[] ObjectLabel { get; set; } + public string OutputName { get; set; } public string OutputBaseDir { get; set; } diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index 138365b2af..46a37019ff 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -34,13 +34,13 @@ TextTemplatingFilePreprocessor AzureModelBuilder.cs - - AzureImageModelOutputClass.cs + + AzureObjectDetectionModelOutputClass.cs TextTemplatingFilePreprocessor - + + AzureObjectDetectionModelOutput.cs TextTemplatingFilePreprocessor - AzureImageModelOutputClass.cs TextTemplatingFilePreprocessor @@ -50,10 +50,6 @@ TextTemplatingFilePreprocessor ObjectDetectionOnnxMapping.cs - - TextTemplatingFilePreprocessor - OnnxModelOutputClass.cs - TextTemplatingFilePreprocessor ReshapeTransformer.cs @@ -100,6 +96,11 @@ True AzureModelBuilder.tt + + AzureImageModelOutputClass.tt + True + True + True True @@ -110,21 +111,16 @@ True True - + True True - AzureImageModelOutputClass.tt + AzureObjectDetectionModelOutputClass.tt True True LabelMapping.tt - - True - True - OnnxModelOutputClass.tt - True True diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs similarity index 80% rename from src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs rename to src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs index 037dca3033..c0a4096388 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs @@ -18,7 +18,7 @@ namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model /// Class to produce the template output /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal partial class AzureImageModelOutputClass : AzureImageModelOutputClassBase + internal partial class AzureObjectDetectionModelOutputClass : AzureObjectDetectionModelOutputClassBase { /// /// Create the template output @@ -30,41 +30,41 @@ public virtual string TransformText() } else if(Target == CSharp.GenerateTarget.ModelBuilder){ MB_Annotation(); } - this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\nusing System.Linq;\r\n\r\nnamespace "); + this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\nusing System.Collections.Generic;\r\nusi" + + "ng System.Linq;\r\n\r\nnamespace "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n [ColumnName(\"output1\")]\r\n" + - " public float[] Output1 { get; set; }\r\n\r\n public string[] Classifi" + - "cationLabels = new string[]{"); + this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n public string[] Classific" + + "ationLabels = new string[]{"); foreach(var label in Labels){ this.Write("\""); this.Write(this.ToStringHelper.ToStringWithCulture(label)); this.Write("\","); } - this.Write(@"}; - - public string Prediction - { - get - { - var maxScore = this.Score.Max(); - var maxIndex = Array.IndexOf(this.Score, maxScore); - return this.ClassificationLabels[maxIndex]; - } - } - - public float[] Score - { - get - { - var exp = this.Output1.Select(x => (float)Math.Exp(x)); - var exp_sum = exp.Sum(); - return exp.Select(x => x / exp_sum).ToArray(); - } - } - } -} - -"); + this.Write("};\r\n\r\n [ColumnName(\"boxes\")]\r\n public float[] Boxes { get; set; }\r\n" + + "\r\n [ColumnName(\"PredictedLabels\")]\r\n public string[] Labels { get;" + + " set; }\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; " + + "set; }\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n ge" + + "t\r\n {\r\n var boundingBoxes = new List();\r\n" + + "\r\n boundingBoxes = Enumerable.Range(0, this.Labels.Length)\r\n " + + " .Select((index) =>\r\n {\r\n " + + " var boxes = this.Boxes;\r\n var s" + + "cores = this.Scores;\r\n var labels = this.Labels;\r\n\r" + + "\n return new BoundingBox()\r\n " + + " {\r\n Left = boxes[index * 4],\r\n " + + " Top = boxes[(index * 4) + 1],\r\n " + + " Right = boxes[(index * 4) + 2],\r\n Bott" + + "om = boxes[(index * 4) + 3],\r\n Score = scores[i" + + "ndex],\r\n Label = labels[index].ToString(),\r\n " + + " };\r\n }).ToList();\r\n " + + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n publ" + + "ic override string ToString()\r\n {\r\n return string.Join(\"\\n\", B" + + "oundingBoxes.Select(x => x.ToString()));\r\n }\r\n }\r\n\r\n public class B" + + "oundingBox\r\n {\r\n public float Top;\r\n\r\n public float Left;\r\n\r\n " + + " public float Right;\r\n\r\n public float Bottom;\r\n\r\n public stri" + + "ng Label;\r\n\r\n public float Score;\r\n\r\n public override string ToStr" + + "ing()\r\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}, Right" + + ": {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}\"" + + ";\r\n }\r\n }\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } @@ -99,7 +99,7 @@ void MB_Annotation() /// Base class for this transformation /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal class AzureImageModelOutputClassBase + internal class AzureObjectDetectionModelOutputClassBase { #region Fields private global::System.Text.StringBuilder generationEnvironmentField; diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt new file mode 100644 index 0000000000..94dff5f275 --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt @@ -0,0 +1,89 @@ +<#@ template language="C#" linePragmas="false" visibility="internal"#> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> +<#CLI_Annotation();#> +<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> +<#MB_Annotation();#> +<# } #> + +using System; +using Microsoft.ML.Data; +using System.Collections.Generic; +using System.Linq; + +namespace <#= Namespace #>.Model +{ + public class ModelOutput + { + public string[] ClassificationLabels = new string[]{<#foreach(var label in Labels){#>"<#=label#>",<#}#>}; + + [ColumnName("boxes")] + public float[] Boxes { get; set; } + + [ColumnName("PredictedLabels")] + public string[] Labels { get; set; } + + [ColumnName("scores")] + public float[] Scores { get; set; } + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + + boundingBoxes = Enumerable.Range(0, this.Labels.Length) + .Select((index) => + { + var boxes = this.Boxes; + var scores = this.Scores; + var labels = this.Labels; + + return new BoundingBox() + { + Left = boxes[index * 4], + Top = boxes[(index * 4) + 1], + Right = boxes[(index * 4) + 2], + Bottom = boxes[(index * 4) + 3], + Score = scores[index], + Label = labels[index].ToString(), + }; + }).ToList(); + return boundingBoxes.ToArray(); + } + } + + public override string ToString() + { + return string.Join("\n", BoundingBoxes.Select(x => x.ToString())); + } + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } +} +<#+ +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} +public string[] Labels {get; set;} = new string[0]; +#> \ No newline at end of file diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/OnnxModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/OnnxModelOutputClass.tt deleted file mode 100644 index 377cfa74e8..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/OnnxModelOutputClass.tt +++ /dev/null @@ -1,52 +0,0 @@ -<#@ template language="C#" linePragmas="false" visibility="internal"#> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> -<#CLI_Annotation();#> -<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> -<#MB_Annotation();#> -<# } #> -using System; -using Microsoft.ML.Data; - -namespace <#= Namespace #>.Model -{ - public class ModelOutput - { -<#if("BinaryClassification".Equals(TaskType)){ #> - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field. - [ColumnName("label")] - public <#= PredictionLabelType#>[] Prediction { get; set; } - -<# } if("MulticlassClassification".Equals(TaskType)){ #> - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field. - [ColumnName("label")] - public <#= PredictionLabelType#>[] Prediction { get; set; } -<# } if("AzureImageClassification".Equals(TaskType)){ #> - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field. - [ColumnName("output1")] - public float[] Score { get; set; } -<# } -#> -<#if("MulticlassClassification".Equals(TaskType)){ #> - [ColumnName("probabilities")] - public float[] Score { get; set; } -<#}else if (!"AzureImageClassification".Equals(TaskType)){ #> - [ColumnName("probabilities")] - public float[] Score { get; set; } -<#}#> - - } -} - -<#+ -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -public string TaskType {get;set;} -public string PredictionLabelType {get;set;} -#> \ No newline at end of file diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs index 91ff06a5c2..5d6ca36045 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs @@ -44,7 +44,7 @@ public class ConsumeModel public static string MLNetModelPath = Path.GetFullPath("""); this.Write(this.ToStringHelper.ToStringWithCulture(MLNetModelName)); this.Write("\");\r\n"); -if(IsAzureImage){ +if(IsAzureImage || IsAzureObjectDetection){ this.Write(" \r\n public static string OnnxModelPath = Path.GetFullPath(\""); this.Write(this.ToStringHelper.ToStringWithCulture(OnnxModelName)); this.Write("\");\r\n"); @@ -63,15 +63,14 @@ public static PredictionEngine CreatePredictionEngine() // Create new MLContext MLContext mlContext = new MLContext(); "); -if(HasReshapeTransformer){ - this.Write(" \r\n\t\t\t// Register ReshapeTransformer to calculate probabilities for each Label.\r\n" + - " mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransforme" + - "r).Assembly);\r\n"); -} -if(HasObjectDetectionLabelMapping){ - this.Write(" \r\n\t\t\t// Register ObjectDetectionMapping to map predicted labels to correct strin" + - "g.\r\n mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetecti" + - "onLabelMapping).Assembly);\r\n"); +if(IsAzureObjectDetection){ + this.Write(@" + // Register ReshapeTransformer to calculate probabilities for each Label. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); + + // Register ObjectDetectionMapping to map predicted labels to correct string. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); +"); } this.Write(@" // Load model & create prediction engine @@ -88,8 +87,7 @@ public static PredictionEngine CreatePredictionEngine() public string Namespace {get;set;} internal CSharp.GenerateTarget Target {get;set;} -public bool HasReshapeTransformer {get; set;}=false; -public bool HasObjectDetectionLabelMapping {get; set;}=false; +public bool IsAzureObjectDetection {get; set;}=false; public bool IsAzureImage {get; set;}=false; public string MLNetModelName {get; set;} public string OnnxModelName {get; set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt index 1fa7d37a0f..9f9da7b8fc 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt @@ -24,7 +24,7 @@ namespace <#= Namespace #>.Model private static Lazy> PredictionEngine = new Lazy>(CreatePredictionEngine); public static string MLNetModelPath = Path.GetFullPath("<#= MLNetModelName #>"); -<#if(IsAzureImage){ #> +<#if(IsAzureImage || IsAzureObjectDetection){ #> public static string OnnxModelPath = Path.GetFullPath("<#= OnnxModelName #>"); <#} #> @@ -40,12 +40,11 @@ namespace <#= Namespace #>.Model { // Create new MLContext MLContext mlContext = new MLContext(); -<#if(HasReshapeTransformer){ #> +<#if(IsAzureObjectDetection){ #> // Register ReshapeTransformer to calculate probabilities for each Label. mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); -<#} #> -<#if(HasObjectDetectionLabelMapping){ #> - // Register ObjectDetectionMapping to map predicted labels to correct string. + + // Register ObjectDetectionMapping to map predicted labels to correct string. mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); <#} #> @@ -60,8 +59,7 @@ namespace <#= Namespace #>.Model <#+ public string Namespace {get;set;} internal CSharp.GenerateTarget Target {get;set;} -public bool HasReshapeTransformer {get; set;}=false; -public bool HasObjectDetectionLabelMapping {get; set;}=false; +public bool IsAzureObjectDetection {get; set;}=false; public bool IsAzureImage {get; set;}=false; public string MLNetModelName {get; set;} public string OnnxModelName {get; set;} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs index 209fe88602..d6f174f7b8 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.cs @@ -74,8 +74,7 @@ public virtual string TransformText() "tputDirectory>PreserveNewest\r\n \r\n"); if (IncludeOnnxModel){ this.Write(" \r\n PreserveNewest\r\n \r\n \r\n " + - " PreserveNewest\r\n \r\n"); + "CopyToOutputDirectory>\r\n \r\n"); } this.Write(" \r\n\r\n \r\n"); if (Target==CSharp.GenerateTarget.Cli) { diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.tt index e46c8953ce..c0f382f9c1 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ModelProject.tt @@ -45,9 +45,6 @@ PreserveNewest - - PreserveNewest - <#}#> diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.Model.csproj.approved.txt index 9ae818e04a..4cd9fba137 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.test.Model.csproj.approved.txt @@ -16,9 +16,6 @@ PreserveNewest - - PreserveNewest - diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt index bf9adefa3a..3bc86db9e3 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -17,9 +17,6 @@ PreserveNewest - - PreserveNewest - diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt index 5a0f249e82..7eeab03036 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.ConsoleApp.csproj.approved.txt @@ -8,6 +8,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt index d2622aecf7..42a5a0cc44 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.CodeGenTest.Model.csproj.approved.txt @@ -6,6 +6,7 @@ + diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt index dbd179ea16..1dec73929d 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.IO; using Microsoft.ML; using CodeGenTest.Model; @@ -13,6 +14,10 @@ namespace CodeGenTest.Model { private static Lazy> PredictionEngine = new Lazy>(CreatePredictionEngine); + public static string MLNetModelPath = Path.GetFullPath("/path/to/model"); + + public static string OnnxModelPath = Path.GetFullPath("/path/to/onnxModel"); + // For more info on consuming ML.NET models, visit https://aka.ms/mlnet-consume // Method for consuming model in your app public static ModelOutput Predict(ModelInput input) @@ -33,8 +38,7 @@ namespace CodeGenTest.Model mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); // Load model & create prediction engine - string modelPath = @"/path/to/model"; - ITransformer mlModel = mlContext.Model.Load(modelPath, out var modelInputSchema); + ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); return predEngine; diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 8a6da5814b..740ec3c8f4 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -11,8 +11,8 @@ namespace CodeGenTest.ConsoleApp public static class ModelBuilder { private static string TRAIN_DATA_FILEPATH = @"/path/to/dataset"; - private static string MLNET_MODEL = @"/path/to/model"; - private static string ONNX_MODEL = @"/path/to/onnxModel"; + private static string MLNET_MODEL = ConsumeModel.MLNetModelPath; + private static string ONNX_MODEL = ConsumeModel.OnnxModelPath; // Create MLContext to be shared across the model creation workflow objects // Set a random seed for repeatable/deterministic results across multiple trainings. diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt index b11d30b740..5f2ffd4c91 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -9,6 +9,8 @@ namespace CodeGenTest.Model { public class ModelOutput { + public string[] ClassificationLabels = new string[] { "label1", "label2", "label3", }; + [ColumnName("boxes")] public float[] Boxes { get; set; } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 4872951a4b..1570aca89d 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -227,6 +227,7 @@ public void AzureImageCodeGeneratorTest() OnnxModelName = @"/path/to/onnxModel", OnnxRuntimePacakgeVersion = "1.2.3", IsAzureAttach = true, + IsObjectDetection = false, IsImage = true, }; var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); @@ -251,19 +252,21 @@ public void AzureObjectDetectionCodeGeneratorTest() var setting = new CodeGeneratorSettings() { TrainDataset = @"/path/to/dataset", - ModelPath = @"/path/to/model", - MlTask = TaskKind.MulticlassClassification, + ModelName = @"/path/to/model", + MlTask = TaskKind.ObjectDetection, OutputName = @"CodeGenTest", OutputBaseDir = @"/path/to/codegen", LabelName = "Label", Target = GenerateTarget.ModelBuilder, StablePackageVersion = "stableversion", UnstablePackageVersion = "unstableversion", - OnnxModelPath = @"/path/to/onnxModel", + OnnxModelName = @"/path/to/onnxModel", + OnnxRuntimePacakgeVersion = @"1.2.3", IsAzureAttach = true, IsImage = false, IsObjectDetection = true, ClassificationLabel = new string[] { "label1", "label2", "label3" }, + ObjectLabel = new string[] { "label1", "label2", "label3" }, }; var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); //codeGen.GenerateOutput(); // use this to see output in project. @@ -301,6 +304,7 @@ public void AzureCodeGeneratorTest() OnnxRuntimePacakgeVersion = "1.2.3", IsAzureAttach = true, IsImage = false, + IsObjectDetection = false, OnnxInputMapping = mapping, }; var codeGen = new AzureAttachCodeGenenrator(pipeline, columnInference, setting); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt new file mode 100644 index 0000000000..c3367ded33 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt @@ -0,0 +1,79 @@ +//***************************************************************************************** +//* * +//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * +//* * +//***************************************************************************************** + +using System; +using Microsoft.ML.Data; +using System.Collections.Generic; +using System.Linq; + +namespace Namespace.Model +{ + public class ModelOutput + { + public string[] ClassificationLabels = new string[]{"str1","str2","str3",}; + + [ColumnName("boxes")] + public float[] Boxes { get; set; } + + [ColumnName("PredictedLabels")] + public string[] Labels { get; set; } + + [ColumnName("scores")] + public float[] Scores { get; set; } + + private BoundingBox[] BoundingBoxes + { + get + { + var boundingBoxes = new List(); + + boundingBoxes = Enumerable.Range(0, this.Labels.Length) + .Select((index) => + { + var boxes = this.Boxes; + var scores = this.Scores; + var labels = this.Labels; + + return new BoundingBox() + { + Left = boxes[index * 4], + Top = boxes[(index * 4) + 1], + Right = boxes[(index * 4) + 2], + Bottom = boxes[(index * 4) + 3], + Score = scores[index], + Label = labels[index].ToString(), + }; + }).ToList(); + return boundingBoxes.ToArray(); + } + } + + public override string ToString() + { + return string.Join("\n", BoundingBoxes.Select(x => x.ToString())); + } + } + + public class BoundingBox + { + public float Top; + + public float Left; + + public float Right; + + public float Bottom; + + public string Label; + + public float Score; + + public override string ToString() + { + return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt new file mode 100644 index 0000000000..c85d8bee3d --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt @@ -0,0 +1,47 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Microsoft.ML; +using Namespace.Model; + +namespace Namespace.Model +{ + public class ConsumeModel + { + private static Lazy> PredictionEngine = new Lazy>(CreatePredictionEngine); + + public static string MLNetModelPath = Path.GetFullPath("mlmodel.zip"); + + public static string OnnxModelPath = Path.GetFullPath("onnx.onnx"); + + // For more info on consuming ML.NET models, visit https://aka.ms/mlnet-consume + // Method for consuming model in your app + public static ModelOutput Predict(ModelInput input) + { + ModelOutput result = PredictionEngine.Value.Predict(input); + return result; + } + + public static PredictionEngine CreatePredictionEngine() + { + // Create new MLContext + MLContext mlContext = new MLContext(); + + // Register ReshapeTransformer to calculate probabilities for each Label. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); + + // Register ObjectDetectionMapping to map predicted labels to correct string. + mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); + + // Load model & create prediction engine + ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); + var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); + + return predEngine; + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_NotAzureImage_NotObjectDetection.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_NotAzureImage_NotObjectDetection.approved.txt new file mode 100644 index 0000000000..b1e4146d53 --- /dev/null +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_NotAzureImage_NotObjectDetection.approved.txt @@ -0,0 +1,39 @@ +// This file was auto-generated by ML.NET Model Builder. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Microsoft.ML; +using Namespace.Model; + +namespace Namespace.Model +{ + public class ConsumeModel + { + private static Lazy> PredictionEngine = new Lazy>(CreatePredictionEngine); + + public static string MLNetModelPath = Path.GetFullPath("mlmodel.zip"); + + // For more info on consuming ML.NET models, visit https://aka.ms/mlnet-consume + // Method for consuming model in your app + public static ModelOutput Predict(ModelInput input) + { + ModelOutput result = PredictionEngine.Value.Predict(input); + return result; + } + + public static PredictionEngine CreatePredictionEngine() + { + // Create new MLContext + MLContext mlContext = new MLContext(); + + // Load model & create prediction engine + ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); + var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); + + return predEngine; + } + } +} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs index e5006ac9c9..b87ed8d1aa 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.cs @@ -44,12 +44,13 @@ public void TestPredictProgram_WithSampleData() [Fact] [UseReporter(typeof(DiffReporter))] [MethodImpl(MethodImplOptions.NoInlining)] - public void TestConsumeModel_NonAzureImage() + public void TestConsumeModel_NotAzureImage_NotObjectDetection() { var consumeModel = new ConsumeModel() { Namespace = "Namespace", IsAzureImage = false, + IsAzureObjectDetection = false, MLNetModelName = @"mlmodel.zip", }; @@ -65,6 +66,24 @@ public void TestConsumeModel_AzureImage() { Namespace = "Namespace", IsAzureImage = true, + IsAzureObjectDetection = false, + MLNetModelName = @"mlmodel.zip", + OnnxModelName = "onnx.onnx" + }; + + Approvals.Verify(consumeModel.TransformText()); + } + + [Fact] + [UseReporter(typeof(DiffReporter))] + [MethodImpl(MethodImplOptions.NoInlining)] + public void TestConsumeModel_AzureObjectDetection() + { + var consumeModel = new ConsumeModel() + { + Namespace = "Namespace", + IsAzureImage = false, + IsAzureObjectDetection = true, MLNetModelName = @"mlmodel.zip", OnnxModelName = "onnx.onnx" }; @@ -87,6 +106,21 @@ public void TestAzureImageModelOutputClass_WithLabel() Approvals.Verify(azureImageOutput.TransformText()); } + [Fact] + [UseReporter(typeof(DiffReporter))] + [MethodImpl(MethodImplOptions.NoInlining)] + public void TestAzureObjectDetectionModelOutputClass_WithLabel() + { + var azureImageOutput = new AzureObjectDetectionModelOutputClass + { + Namespace = "Namespace", + Labels = new string[] { "str1", "str2", "str3" }, + Target = CSharp.GenerateTarget.Cli + }; + + Approvals.Verify(azureImageOutput.TransformText()); + } + [Fact] [UseReporter(typeof(DiffReporter))] [MethodImpl(MethodImplOptions.NoInlining)] From 45c462acbdea3505792046bf5679238d1d2fb510 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Tue, 21 Jul 2020 15:46:08 -0700 Subject: [PATCH 23/28] fix build error --- .../CSharp/TransformGenerators.cs | 2 +- .../Azure/Model/AzureImageModelOutputClass.cs | 369 ++++++++++++++++++ 2 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index e040b23fb5..4b867bdbf8 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -423,7 +423,7 @@ public override string GenerateTransformer() return $"ExtractPixels({outputColumn}, {inputColumn})"; } } - + internal class ObjectDetectionPixelExtract : TransformGeneratorBase { public ObjectDetectionPixelExtract(PipelineNode node) : base(node) { } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs new file mode 100644 index 0000000000..037dca3033 --- /dev/null +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureImageModelOutputClass.cs @@ -0,0 +1,369 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 16.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using System; + + /// + /// Class to produce the template output + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal partial class AzureImageModelOutputClass : AzureImageModelOutputClassBase + { + /// + /// Create the template output + /// + public virtual string TransformText() + { +if(Target == CSharp.GenerateTarget.Cli){ +CLI_Annotation(); + } else if(Target == CSharp.GenerateTarget.ModelBuilder){ +MB_Annotation(); + } + this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\nusing System.Linq;\r\n\r\nnamespace "); + this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); + this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n [ColumnName(\"output1\")]\r\n" + + " public float[] Output1 { get; set; }\r\n\r\n public string[] Classifi" + + "cationLabels = new string[]{"); +foreach(var label in Labels){ + this.Write("\""); + this.Write(this.ToStringHelper.ToStringWithCulture(label)); + this.Write("\","); +} + this.Write(@"}; + + public string Prediction + { + get + { + var maxScore = this.Score.Max(); + var maxIndex = Array.IndexOf(this.Score, maxScore); + return this.ClassificationLabels[maxIndex]; + } + } + + public float[] Score + { + get + { + var exp = this.Output1.Select(x => (float)Math.Exp(x)); + var exp_sum = exp.Sum(); + return exp.Select(x => x / exp_sum).ToArray(); + } + } + } +} + +"); + return this.GenerationEnvironment.ToString(); + } + +public string Namespace {get;set;} +internal CSharp.GenerateTarget Target {get;set;} +public string[] Labels {get; set;} = new string[0]; + + +void CLI_Annotation() +{ +this.Write(@"//***************************************************************************************** +//* * +//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * +//* * +//***************************************************************************************** +"); + + +} + + +void MB_Annotation() +{ +this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); + + +} + + } + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] + internal class AzureImageModelOutputClassBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} From 445f9825e2a9302b40fad62eb33f384373c9e3d7 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Wed, 22 Jul 2020 14:53:35 -0700 Subject: [PATCH 24/28] fix e2e bug --- .../AzureAttachModelCodeGenerator.cs | 26 -- .../CSharp/TransformGeneratorFactory.cs | 20 +- .../CSharp/TransformGenerators.cs | 51 --- .../Microsoft.ML.CodeGenerator.csproj | 27 +- .../AzureObjectDetectionModelOutputClass.cs | 52 +-- .../AzureObjectDetectionModelOutputClass.tt | 8 +- .../Azure/Model/ObjectDetectionOnnxMapping.cs | 374 ----------------- .../Azure/Model/ObjectDetectionOnnxMapping.tt | 101 ----- .../Azure/Model/ReshapeTransformer.cs | 378 ------------------ .../Azure/Model/ReshapeTransformer.tt | 62 --- .../Templates/Console/ConsumeModel.cs | 12 +- .../Templates/Console/ConsumeModel.tt | 7 - ...GeneratorTest.ConsumeModel.cs.approved.txt | 6 - ...GeneratorTest.ModelBuilder.cs.approved.txt | 10 +- ...eGeneratorTest.ModelOutput.cs.approved.txt | 8 +- .../ConsoleCodeGeneratorTests.cs | 23 +- ...ionModelOutputClass_WithLabel.approved.txt | 8 +- ...umeModel_AzureObjectDetection.approved.txt | 6 - 18 files changed, 53 insertions(+), 1126 deletions(-) delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index 53b9746f2f..3b5be4f077 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -31,8 +31,6 @@ internal class AzureAttachModelCodeGenerator : ICSharpProjectGenerator public ICSharpFile ConsumeModel { get; private set; } public ICSharpFile LabelMapping { get; private set; } public ICSharpFile ImageLabelMapping { get; private set; } - public ICSharpFile ObjectDetectionLabelMapping { get; private set; } - public ICSharpFile ReshapeTransformerMap { get; private set; } public ICSharpFile ObjectDetectionConsumeModel { get; private set; } public string Name { get; set; } @@ -113,28 +111,6 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c Name = "LabelMapping.cs", }; - // Tevin: added - ObjectDetectionLabelMapping = new CSharpCodeFile() - { - File = new ObjectDetectionOnnxMapping() - { - Target = _settings.Target, - Namespace = _nameSpaceValue, - ImportLabels = _settings.ClassificationLabel, - }.TransformText(), - Name = "ObjectDetectionLabelMapping.cs", - }; - - ReshapeTransformerMap = new CSharpCodeFile() - { - File = new ReshapeTransformer() - { - Target = _settings.Target, - Namespace = _nameSpaceValue, - }.TransformText(), - Name = "ReshapeTransformer.cs", - }; - ConsumeModel = new CSharpCodeFile() { File = new ConsumeModel() @@ -170,8 +146,6 @@ public ICSharpProject ToProject() AzureObjectDetectionModelOutputClass, ConsumeModel, ModelProject, - ReshapeTransformerMap, - ObjectDetectionLabelMapping, }; } else diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs index eac0bc8e54..ddf8bf07eb 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGeneratorFactory.cs @@ -20,13 +20,9 @@ internal enum SpecialTransformer ApplyOnnxModel = 0, ResizeImage = 1, ExtractPixel = 2, - ObjectDetectionLabelMapping = 3, - ReshapeTransformer = 4, - ObjectDetectionResizeImage = 5, - ObjectDetectionExtractPixel = 6, - ApplyObjectDetectionModel = 7, - + ObjectDetectionResizeImage = 3, } + internal static class TransformGeneratorFactory { internal static ITransformGenerator GetInstance(PipelineNode node) @@ -98,21 +94,9 @@ internal static ITransformGenerator GetInstance(PipelineNode node) case SpecialTransformer.ApplyOnnxModel: result = new ApplyOnnxModel(node); break; - case SpecialTransformer.ObjectDetectionLabelMapping: - result = new CustomObjectDetectionLabelMapping(node); - break; - case SpecialTransformer.ObjectDetectionExtractPixel: - result = new ObjectDetectionPixelExtract(node); - break; case SpecialTransformer.ObjectDetectionResizeImage: result = new ObjectDetectionImageResizing(node); break; - case SpecialTransformer.ApplyObjectDetectionModel: - result = new ApplyObjectDetectionOnnxModel(node); - break; - case SpecialTransformer.ReshapeTransformer: - result = new CustomReshapeTransformer(node); - break; default: return null; } diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index 4b867bdbf8..1e45651102 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -424,57 +424,6 @@ public override string GenerateTransformer() } } - internal class ObjectDetectionPixelExtract : TransformGeneratorBase - { - public ObjectDetectionPixelExtract(PipelineNode node) : base(node) { } - internal override string MethodName => "ExtractPixels"; - - public override string GenerateTransformer() - { - return @"ExtractPixels(outputColumnName: ""input"", inputColumnName: ""input"")"; - } - } - - // Tevin: added to include OD label mapping - internal class CustomObjectDetectionLabelMapping: TransformGeneratorBase - { - public CustomObjectDetectionLabelMapping(PipelineNode node) : base(node) { } - internal override string MethodName => "ObjectDetectionLabelMapping"; - - public override string GenerateTransformer() - { - return @"CustomMapping( - (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), - contractName: nameof(ObjectDetectionLabelMapping))"; - } - } - - // Tevin: added to have the reshaper for OD - internal class CustomReshapeTransformer : TransformGeneratorBase - { - public CustomReshapeTransformer(PipelineNode node) : base(node) { } - internal override string MethodName => "ReshapeTransformer"; - - public override string GenerateTransformer() - { - return @"CustomMapping( - (input, output) => ReshapeTransformer.Mapping(input, output), - contractName: nameof(ReshapeTransformer))"; - } - } - - // Tevin: added to have correct ApplyOnnxModel - internal class ApplyObjectDetectionOnnxModel : TransformGeneratorBase - { - public ApplyObjectDetectionOnnxModel(PipelineNode node) : base(node) { } - internal override string MethodName => "ApplyOnnxModel"; - - public override string GenerateTransformer() - { - return @"ApplyOnnxModel(modelFile: ONNX_MODEL, outputColumnNames: new[] { ""boxes"",""labels"", ""scores"" }, inputColumnNames: new[] { ""input"" })"; - } - } - internal class ApplyOnnxModel : TransformGeneratorBase { public ApplyOnnxModel(PipelineNode node) : base(node) { } diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index 46a37019ff..bf768aed26 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -11,14 +11,6 @@ - - - True - True - ReshapeTransformer.tt - - - @@ -39,21 +31,13 @@ TextTemplatingFilePreprocessor - AzureObjectDetectionModelOutput.cs + AzureImageModelOutputClass.cs TextTemplatingFilePreprocessor TextTemplatingFilePreprocessor LabelMapping.cs - - TextTemplatingFilePreprocessor - ObjectDetectionOnnxMapping.cs - - - TextTemplatingFilePreprocessor - ReshapeTransformer.cs - @@ -101,10 +85,10 @@ True True - + True True - ObjectDetectionOnnxMapping.tt + AzureImageModelOutputClass.tt OnnxModelOutputClass.tt @@ -121,11 +105,6 @@ True LabelMapping.tt - - True - True - ReshapeTransformer.tt - True True diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs index c0a4096388..65b87d4727 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.cs @@ -33,38 +33,38 @@ public virtual string TransformText() this.Write("\r\nusing System;\r\nusing Microsoft.ML.Data;\r\nusing System.Collections.Generic;\r\nusi" + "ng System.Linq;\r\n\r\nnamespace "); this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n public string[] Classific" + - "ationLabels = new string[]{"); + this.Write(".Model\r\n{\r\n public class ModelOutput\r\n {\r\n public string[] ObjectTag" + + "s = new string[]{"); foreach(var label in Labels){ this.Write("\""); this.Write(this.ToStringHelper.ToStringWithCulture(label)); this.Write("\","); } this.Write("};\r\n\r\n [ColumnName(\"boxes\")]\r\n public float[] Boxes { get; set; }\r\n" + - "\r\n [ColumnName(\"PredictedLabels\")]\r\n public string[] Labels { get;" + - " set; }\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Scores { get; " + - "set; }\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n ge" + - "t\r\n {\r\n var boundingBoxes = new List();\r\n" + - "\r\n boundingBoxes = Enumerable.Range(0, this.Labels.Length)\r\n " + - " .Select((index) =>\r\n {\r\n " + - " var boxes = this.Boxes;\r\n var s" + - "cores = this.Scores;\r\n var labels = this.Labels;\r\n\r" + - "\n return new BoundingBox()\r\n " + - " {\r\n Left = boxes[index * 4],\r\n " + - " Top = boxes[(index * 4) + 1],\r\n " + - " Right = boxes[(index * 4) + 2],\r\n Bott" + - "om = boxes[(index * 4) + 3],\r\n Score = scores[i" + - "ndex],\r\n Label = labels[index].ToString(),\r\n " + - " };\r\n }).ToList();\r\n " + - " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n publ" + - "ic override string ToString()\r\n {\r\n return string.Join(\"\\n\", B" + - "oundingBoxes.Select(x => x.ToString()));\r\n }\r\n }\r\n\r\n public class B" + - "oundingBox\r\n {\r\n public float Top;\r\n\r\n public float Left;\r\n\r\n " + - " public float Right;\r\n\r\n public float Bottom;\r\n\r\n public stri" + - "ng Label;\r\n\r\n public float Score;\r\n\r\n public override string ToStr" + - "ing()\r\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}, Right" + - ": {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}\"" + - ";\r\n }\r\n }\r\n}\r\n"); + "\r\n [ColumnName(\"labels\")]\r\n public long[] Labels { get; set; }\r\n\r\n" + + " [ColumnName(\"scores\")]\r\n public float[] Scores { get; set; }\r\n\r\n " + + " private BoundingBox[] BoundingBoxes\r\n {\r\n get\r\n " + + " {\r\n var boundingBoxes = new List();\r\n\r\n " + + " boundingBoxes = Enumerable.Range(0, this.Labels.Length)\r\n " + + " .Select((index) =>\r\n {\r\n " + + " var boxes = this.Boxes;\r\n var scores = thi" + + "s.Scores;\r\n var labels = this.Labels;\r\n\r\n " + + " return new BoundingBox()\r\n {\r\n " + + " Left = boxes[index * 4],\r\n " + + " Top = boxes[(index * 4) + 1],\r\n Ri" + + "ght = boxes[(index * 4) + 2],\r\n Bottom = boxes[" + + "(index * 4) + 3],\r\n Score = scores[index],\r\n " + + " Label = this.ObjectTags[labels[index]],\r\n " + + " };\r\n }).ToList();\r\n " + + " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n public ov" + + "erride string ToString()\r\n {\r\n return string.Join(\"\\n\", Boundi" + + "ngBoxes.Select(x => x.ToString()));\r\n }\r\n }\r\n\r\n public class Boundi" + + "ngBox\r\n {\r\n public float Top;\r\n\r\n public float Left;\r\n\r\n " + + " public float Right;\r\n\r\n public float Bottom;\r\n\r\n public string La" + + "bel;\r\n\r\n public float Score;\r\n\r\n public override string ToString()" + + "\r\n {\r\n return $\"Top: {this.Top}, Left: {this.Left}, Right: {th" + + "is.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}\";\r\n " + + " }\r\n }\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt index 94dff5f275..7cf4080a8a 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/AzureObjectDetectionModelOutputClass.tt @@ -18,13 +18,13 @@ namespace <#= Namespace #>.Model { public class ModelOutput { - public string[] ClassificationLabels = new string[]{<#foreach(var label in Labels){#>"<#=label#>",<#}#>}; + public string[] ObjectTags = new string[]{<#foreach(var label in Labels){#>"<#=label#>",<#}#>}; [ColumnName("boxes")] public float[] Boxes { get; set; } - [ColumnName("PredictedLabels")] - public string[] Labels { get; set; } + [ColumnName("labels")] + public long[] Labels { get; set; } [ColumnName("scores")] public float[] Scores { get; set; } @@ -49,7 +49,7 @@ namespace <#= Namespace #>.Model Right = boxes[(index * 4) + 2], Bottom = boxes[(index * 4) + 3], Score = scores[index], - Label = labels[index].ToString(), + Label = this.ObjectTags[labels[index]], }; }).ToList(); return boundingBoxes.ToArray(); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs deleted file mode 100644 index 8b854cf64d..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.cs +++ /dev/null @@ -1,374 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model -{ - using System.Linq; - using System.Text; - using System.Collections.Generic; - using System; - - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal partial class ObjectDetectionOnnxMapping : ObjectDetectionOnnxMappingBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { -if(Target == CSharp.GenerateTarget.Cli){ -CLI_Annotation(); - } else if(Target == CSharp.GenerateTarget.ModelBuilder){ -MB_Annotation(); - } - this.Write("\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML.Transforms;\r\nusing System;\r\nusing " + - "System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace" + - " "); - this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(@".Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[]{"); -foreach(var label in ImportLabels){ - this.Write("\""); - this.Write(this.ToStringHelper.ToStringWithCulture(label)); - this.Write("\","); -} - this.Write("};\r\n\r\n public static void Mapping(ObjectDetectionLabelMappingInput input, " + - "ObjectDetectionLabelMappingOutput output)\r\n {\r\n long[] oldLabe" + - "ls = input.Labels.GetValues().ToArray();\r\n output.Labels = new string" + - "[oldLabels.Length];\r\n output.Scores = input.Scores.GetValues().ToArra" + - "y();\r\n output.Boxes = input.Boxes.GetValues().ToArray();\r\n " + - " for (int i = 0; i < oldLabels.Length; i++)\r\n {\r\n outp" + - "ut.Labels[i] = Label[oldLabels[i]];\r\n }\r\n }\r\n\r\n // This" + - " factory method will be called when loading the model to get the mapping operati" + - "on.\r\n public override Action GetMapping()\r\n {\r\n return Mapping;\r\n " + - " }\r\n }\r\n\r\n public class ObjectDetectionLabelMappingInput\r\n {\r\n " + - " [ColumnName(\"labels\")]\r\n public VBuffer Labels;\r\n\r\n [Co" + - "lumnName(\"scores\")]\r\n public VBuffer Scores;\r\n\r\n [ColumnNam" + - "e(\"boxes\")]\r\n public VBuffer Boxes;\r\n }\r\n\r\n\r\n\r\n public class" + - " ObjectDetectionLabelMappingOutput\r\n {\r\n [ColumnName(\"boxes\")]\r\n " + - " public float[] Boxes;\r\n\r\n [ColumnName(\"PredictedLabels\")]\r\n publ" + - "ic string[] Labels;\r\n\r\n [ColumnName(\"scores\")]\r\n public float[] Sc" + - "ores;\r\n\r\n private BoundingBox[] BoundingBoxes\r\n {\r\n get" + - "\r\n {\r\n var boundingBoxes = new List();\r\n " + - " return boundingBoxes.ToArray();\r\n }\r\n }\r\n\r\n " + - " public BoundingBox[] GetBoundingBoxes()\r\n {\r\n return this." + - "BoundingBoxes;\r\n }\r\n\r\n public override string ToString()\r\n " + - "{\r\n var sb = new StringBuilder();\r\n\r\n foreach (var box in " + - "this.BoundingBoxes)\r\n {\r\n sb.AppendLine(box.ToString()" + - ");\r\n }\r\n\r\n return sb.ToString();\r\n }\r\n }\r\n}\r\n"); - return this.GenerationEnvironment.ToString(); - } - -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -public string[] ImportLabels {get; set;} = new string[0]; - - -void CLI_Annotation() -{ -this.Write(@"//***************************************************************************************** -//* * -//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * -//* * -//***************************************************************************************** -"); - - -} - - -void MB_Annotation() -{ -this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); - - -} - - } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal class ObjectDetectionOnnxMappingBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt deleted file mode 100644 index 223c4b1ddd..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ObjectDetectionOnnxMapping.tt +++ /dev/null @@ -1,101 +0,0 @@ -<#@ template language="C#" linePragmas="false" visibility="internal" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> -<#CLI_Annotation();#> -<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> -<#MB_Annotation();#> -<# } #> - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace <#= Namespace #>.Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[]{<#foreach(var label in ImportLabels){#>"<#=label#>",<#}#>}; - - public static void Mapping(ObjectDetectionLabelMappingInput input, ObjectDetectionLabelMappingOutput output) - { - long[] oldLabels = input.Labels.GetValues().ToArray(); - output.Labels = new string[oldLabels.Length]; - output.Scores = input.Scores.GetValues().ToArray(); - output.Boxes = input.Boxes.GetValues().ToArray(); - for (int i = 0; i < oldLabels.Length; i++) - { - output.Labels[i] = Label[oldLabels[i]]; - } - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class ObjectDetectionLabelMappingInput - { - [ColumnName("labels")] - public VBuffer Labels; - - [ColumnName("scores")] - public VBuffer Scores; - - [ColumnName("boxes")] - public VBuffer Boxes; - } - - - - public class ObjectDetectionLabelMappingOutput - { - [ColumnName("boxes")] - public float[] Boxes; - - [ColumnName("PredictedLabels")] - public string[] Labels; - - [ColumnName("scores")] - public float[] Scores; - - private BoundingBox[] BoundingBoxes - { - get - { - var boundingBoxes = new List(); - return boundingBoxes.ToArray(); - } - } - - public BoundingBox[] GetBoundingBoxes() - { - return this.BoundingBoxes; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var box in this.BoundingBoxes) - { - sb.AppendLine(box.ToString()); - } - - return sb.ToString(); - } - } -} -<#+ -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -public string[] ImportLabels {get; set;} = new string[0]; -#> \ No newline at end of file diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs deleted file mode 100644 index abcbc5ceb6..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.cs +++ /dev/null @@ -1,378 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model -{ - using System.Linq; - using System.Text; - using System.Collections.Generic; - using System; - - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal partial class ReshapeTransformer : ReshapeTransformerBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { -if(Target == CSharp.GenerateTarget.Cli){ -CLI_Annotation(); - } else if(Target == CSharp.GenerateTarget.ModelBuilder){ -MB_Annotation(); - } - this.Write("\r\nusing System;\r\nusing System.Linq;\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML" + - ".Transforms;\r\n\r\nnamespace "); - this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(@".Model -{ - [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - public class ReshapeTransformer : CustomMappingFactory - { - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(Input input, Output output) - { - var values = input.Reshape.GetValues().ToArray(); - - for (int x = 0; x < values.Count(); x++) - { - // normalize from [0, 255] to [0, 1] - values[x] /= 255; - }; - - output.Reshape = new VBuffer(values.Count(), values); - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class Input - { - [ColumnName(""input"")] - [VectorType(3, 800, 600)] - public VBuffer Reshape; - } - - public class Output - { - [ColumnName(""input"")] - [VectorType(1 * 3 * 600 * 800)] - public VBuffer Reshape; - } -} - -"); - return this.GenerationEnvironment.ToString(); - } - -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} - - -void CLI_Annotation() -{ -this.Write(@"//***************************************************************************************** -//* * -//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * -//* * -//***************************************************************************************** -"); - - -} - - -void MB_Annotation() -{ -this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); - - -} - - } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal class ReshapeTransformerBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt deleted file mode 100644 index e7f352ba3e..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/ReshapeTransformer.tt +++ /dev/null @@ -1,62 +0,0 @@ -<#@ template language="C#" linePragmas="false" visibility="internal" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> -<#CLI_Annotation();#> -<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> -<#MB_Annotation();#> -<# } #> - -using System; -using System.Linq; -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; - -namespace <#= Namespace #>.Model -{ - [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - public class ReshapeTransformer : CustomMappingFactory - { - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(Input input, Output output) - { - var values = input.Reshape.GetValues().ToArray(); - - for (int x = 0; x < values.Count(); x++) - { - // normalize from [0, 255] to [0, 1] - values[x] /= 255; - }; - - output.Reshape = new VBuffer(values.Count(), values); - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class Input - { - [ColumnName("input")] - [VectorType(3, 800, 600)] - public VBuffer Reshape; - } - - public class Output - { - [ColumnName("input")] - [VectorType(1 * 3 * 600 * 800)] - public VBuffer Reshape; - } -} - -<#+ -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -#> - diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs index 5d6ca36045..6ba08042c9 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.cs @@ -62,17 +62,7 @@ public static PredictionEngine CreatePredictionEngine() { // Create new MLContext MLContext mlContext = new MLContext(); -"); -if(IsAzureObjectDetection){ - this.Write(@" - // Register ReshapeTransformer to calculate probabilities for each Label. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); - - // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); -"); -} - this.Write(@" + // Load model & create prediction engine ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt index 9f9da7b8fc..8c3d11568d 100644 --- a/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt +++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/ConsumeModel.tt @@ -40,13 +40,6 @@ namespace <#= Namespace #>.Model { // Create new MLContext MLContext mlContext = new MLContext(); -<#if(IsAzureObjectDetection){ #> - // Register ReshapeTransformer to calculate probabilities for each Label. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); - - // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); -<#} #> // Load model & create prediction engine ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt index 1dec73929d..102d17059d 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ConsumeModel.cs.approved.txt @@ -31,12 +31,6 @@ namespace CodeGenTest.Model // Create new MLContext MLContext mlContext = new MLContext(); - // Register ReshapeTransformer to calculate probabilities for each Label. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); - - // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); - // Load model & create prediction engine ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); var predEngine = mlContext.Model.CreatePredictionEngine(mlModel); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 740ec3c8f4..66f314d831 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -47,14 +47,8 @@ namespace CodeGenTest.ConsoleApp // 2. Map scores to labels to make model output easier to understand and use var pipeline = mlContext.Transforms.LoadImages("input", null, "ImagePath") .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: 800, imageHeight: 600, inputColumnName: "input")) - .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", inputColumnName: "input")) - .Append(mlContext.Transforms.CustomMapping( - (input, output) => ReshapeTransformer.Mapping(input, output), - contractName: nameof(ReshapeTransformer))) - .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL, outputColumnNames: new[] { "boxes", "labels", "scores" }, inputColumnNames: new[] { "input" })) - .Append(mlContext.Transforms.CustomMapping( - (input, output) => ObjectDetectionLabelMapping.Mapping(input, output), - contractName: nameof(ObjectDetectionLabelMapping))); + .Append(mlContext.Transforms.ExtractPixels("input", "input")) + .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL)); return pipeline; } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt index 5f2ffd4c91..0810b19fc5 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelOutput.cs.approved.txt @@ -9,13 +9,13 @@ namespace CodeGenTest.Model { public class ModelOutput { - public string[] ClassificationLabels = new string[] { "label1", "label2", "label3", }; + public string[] ObjectTags = new string[] { "label1", "label2", "label3", }; [ColumnName("boxes")] public float[] Boxes { get; set; } - [ColumnName("PredictedLabels")] - public string[] Labels { get; set; } + [ColumnName("labels")] + public long[] Labels { get; set; } [ColumnName("scores")] public float[] Scores { get; set; } @@ -40,7 +40,7 @@ namespace CodeGenTest.Model Right = boxes[(index * 4) + 2], Bottom = boxes[(index * 4) + 3], Score = scores[index], - Label = labels[index].ToString(), + Label = this.ObjectTags[labels[index]], }; }).ToList(); return boundingBoxes.ToArray(); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs index 1570aca89d..32558e8bf8 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.cs @@ -665,38 +665,29 @@ private CodeGenerator PrepareForRecommendationTask() private (Pipeline, ColumnInferenceResults) GetMockedAzureObjectDetectionPipelineAndInference(string onnxModelPath) { var onnxPipeLineNode = new PipelineNode( - nameof(SpecialTransformer.ApplyObjectDetectionModel), + nameof(SpecialTransformer.ApplyOnnxModel), PipelineNodeType.Transform, - new[] { "input" }, - new[] { "boxes", "labels", "scores" }, - new Dictionary() - { - { "outputColumnNames", new List() { "boxes", "labels", "scores" } }, - { "inputColumnNames", "input" }, - { "modelFile", "awesomeModel.onnx" }, // it doesn't matter what modelFile is - }); + new string[] { }, + new string[] { }, + null); var loadImageNode = new PipelineNode(EstimatorName.ImageLoading.ToString(), PipelineNodeType.Transform, "ImagePath", "input"); var resizeImageNode = new PipelineNode( nameof(SpecialTransformer.ObjectDetectionResizeImage), PipelineNodeType.Transform, - onnxModelPath, - onnxModelPath, + "input", + "input", new Dictionary() { { "imageWidth", 800 }, { "imageHeight", 600 }, }); - var extractPixelsNode = new PipelineNode(nameof(SpecialTransformer.ObjectDetectionExtractPixel), PipelineNodeType.Transform, onnxModelPath, onnxModelPath); - var normalizeMapping = new PipelineNode(nameof(SpecialTransformer.ReshapeTransformer), PipelineNodeType.Transform, string.Empty, string.Empty); - var labelMapping = new PipelineNode(nameof(SpecialTransformer.ObjectDetectionLabelMapping), PipelineNodeType.Transform, string.Empty, string.Empty); + var extractPixelsNode = new PipelineNode(nameof(SpecialTransformer.ExtractPixel), PipelineNodeType.Transform, "input", "input"); var bestPipeLine = new Pipeline(new PipelineNode[] { loadImageNode, resizeImageNode, extractPixelsNode, - normalizeMapping, onnxPipeLineNode, - labelMapping, }); var textLoaderArgs = new TextLoader.Options() diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt index c3367ded33..45519fa4a8 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestAzureObjectDetectionModelOutputClass_WithLabel.approved.txt @@ -13,13 +13,13 @@ namespace Namespace.Model { public class ModelOutput { - public string[] ClassificationLabels = new string[]{"str1","str2","str3",}; + public string[] ObjectTags = new string[]{"str1","str2","str3",}; [ColumnName("boxes")] public float[] Boxes { get; set; } - [ColumnName("PredictedLabels")] - public string[] Labels { get; set; } + [ColumnName("labels")] + public long[] Labels { get; set; } [ColumnName("scores")] public float[] Scores { get; set; } @@ -44,7 +44,7 @@ namespace Namespace.Model Right = boxes[(index * 4) + 2], Bottom = boxes[(index * 4) + 3], Score = scores[index], - Label = labels[index].ToString(), + Label = this.ObjectTags[labels[index]], }; }).ToList(); return boundingBoxes.ToArray(); diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt index c85d8bee3d..a4c874b77b 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/TemplateTest.TestConsumeModel_AzureObjectDetection.approved.txt @@ -30,12 +30,6 @@ namespace Namespace.Model { // Create new MLContext MLContext mlContext = new MLContext(); - - // Register ReshapeTransformer to calculate probabilities for each Label. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ReshapeTransformer).Assembly); - - // Register ObjectDetectionMapping to map predicted labels to correct string. - mlContext.ComponentCatalog.RegisterAssembly(typeof(ObjectDetectionLabelMapping).Assembly); // Load model & create prediction engine ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var modelInputSchema); From 82c672fd6f33f46a4cf76aca6afb6bd044179908 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Wed, 26 Aug 2020 16:44:04 -0700 Subject: [PATCH 25/28] fix e2e bug --- .../CodeGenerator/CSharp/TransformGenerators.cs | 2 +- ...bjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs index 1e45651102..5fdb83bd97 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/TransformGenerators.cs @@ -407,7 +407,7 @@ public ObjectDetectionImageResizing(PipelineNode node) : base(node) { } public override string GenerateTransformer() { - return @"ResizeImages(outputColumnName: ""input"", imageWidth: 800, imageHeight: 600, inputColumnName: ""input"")"; + return @"ResizeImages(outputColumnName: ""ImageSource_featurized"", imageWidth: 800, imageHeight: 600, inputColumnName: ""ImageSource_featurized"")"; } } diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt index 66f314d831..de9170c0f7 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ModelBuilder.cs.approved.txt @@ -46,7 +46,7 @@ namespace CodeGenTest.ConsoleApp // 1. Score using provided onnx model // 2. Map scores to labels to make model output easier to understand and use var pipeline = mlContext.Transforms.LoadImages("input", null, "ImagePath") - .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: 800, imageHeight: 600, inputColumnName: "input")) + .Append(mlContext.Transforms.ResizeImages(outputColumnName: "ImageSource_featurized", imageWidth: 800, imageHeight: 600, inputColumnName: "ImageSource_featurized")) .Append(mlContext.Transforms.ExtractPixels("input", "input")) .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: ONNX_MODEL)); return pipeline; From 9116501997a696180acb7a17449d329e2254ef87 Mon Sep 17 00:00:00 2001 From: Xiaoyun Zhang Date: Thu, 27 Aug 2020 10:55:13 -0700 Subject: [PATCH 26/28] Update Microsoft.ML.CodeGenerator.nupkgproj --- .../Microsoft.ML.CodeGenerator.nupkgproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj b/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj index 6712b551c3..48c288fb6b 100644 --- a/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj +++ b/pkg/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.nupkgproj @@ -6,7 +6,7 @@ - + From 9473c48df28ad8693dc1f901a27ee50b4094da94 Mon Sep 17 00:00:00 2001 From: Xiaoyun Zhang Date: Thu, 27 Aug 2020 10:56:12 -0700 Subject: [PATCH 27/28] Update Microsoft.ML.CodeGenerator.csproj --- .../Microsoft.ML.CodeGenerator.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index bf768aed26..37fc3a02a4 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -18,7 +18,7 @@ - + From ccc5d6ce0c72d40e9e9f39e2654481ce03b05873 Mon Sep 17 00:00:00 2001 From: LittleLittleCloud Date: Thu, 27 Aug 2020 11:09:55 -0700 Subject: [PATCH 28/28] remove .approved.txt that not used --- .../AzureAttachModelCodeGenerator.cs | 14 - .../Microsoft.ML.CodeGenerator.csproj | 9 - .../Templates/Azure/Model/LabelMapping.cs | 394 ------------------ .../Templates/Azure/Model/LabelMapping.tt | 77 ---- ...GeneratorTest.LabelMapping.cs.approved.txt | 42 -- ...GeneratorTest.LabelMapping.cs.approved.txt | 48 --- ...ratorTest.NormalizeMapping.cs.approved.txt | 50 --- ...GeneratorTest.LabelMapping.cs.approved.txt | 104 ----- ...ratorTest.NormalizeMapping.cs.approved.txt | 50 --- ...bjectDetectionLabelMapping.cs.approved.txt | 87 ---- ...ObjectDetectionOnnxMapping.cs.approved.txt | 104 ----- ...ObjectDetectionOnnxMapping.cs.received.txt | 104 ----- ...torTest.ReshapeTransformer.cs.approved.txt | 48 --- 13 files changed, 1131 deletions(-) delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.cs delete mode 100644 src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.tt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.LabelMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.NormalizeMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt delete mode 100644 test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt diff --git a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs index 3b5be4f077..1ac25c2f48 100644 --- a/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs +++ b/src/Microsoft.ML.CodeGenerator/CodeGenerator/CSharp/AzureCodeGenerator/AzureAttachModelCodeGenerator.cs @@ -98,19 +98,6 @@ public AzureAttachModelCodeGenerator(Pipeline pipeline, ColumnInferenceResults c Name = $"{ _settings.OutputName }.Model.csproj", }; - LabelMapping = new CSharpCodeFile() - { - File = new LabelMapping() - { - Target = _settings.Target, - Namespace = _nameSpaceValue, - LabelMappingInputLabelType = typeof(Int64).Name, - PredictionLabelType = labelTypeCsharp.Name, - TaskType = _settings.MlTask.ToString(), - }.TransformText(), - Name = "LabelMapping.cs", - }; - ConsumeModel = new CSharpCodeFile() { File = new ConsumeModel() @@ -156,7 +143,6 @@ public ICSharpProject ToProject() AzureImageModelOutputClass, ConsumeModel, ModelProject, - LabelMapping, }; } project.Name = Name; diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj index 37fc3a02a4..9d823595ca 100644 --- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj +++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj @@ -34,10 +34,6 @@ AzureImageModelOutputClass.cs TextTemplatingFilePreprocessor - - TextTemplatingFilePreprocessor - LabelMapping.cs - @@ -100,11 +96,6 @@ True AzureObjectDetectionModelOutputClass.tt - - True - True - LabelMapping.tt - True True diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.cs b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.cs deleted file mode 100644 index 8d336be8c1..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.cs +++ /dev/null @@ -1,394 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace Microsoft.ML.CodeGenerator.Templates.Azure.Model -{ - using System.Linq; - using System.Text; - using System.Collections.Generic; - using System; - - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal partial class LabelMapping : LabelMappingBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { -if(Target == CSharp.GenerateTarget.Cli){ -CLI_Annotation(); - } else if(Target == CSharp.GenerateTarget.ModelBuilder){ -MB_Annotation(); - } - this.Write("\r\nusing Microsoft.ML.Data;\r\nusing Microsoft.ML.Transforms;\r\nusing System;\r\nusing " + - "System.Linq;\r\n\r\nnamespace "); - this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - this.Write(@".Model -{ - [CustomMappingFactoryAttribute(nameof(LabelMapping))] - public class LabelMapping : CustomMappingFactory - { - // Custom mapping to determine the label with the highest probability - public static void Mapping(LabelMappingInput input, LabelMappingOutput output) - { -"); -if("Boolean".Equals(PredictionLabelType)){ - this.Write("\t\t\toutput.Prediction = input.label.GetValues().ToArray().First() == 1;\r\n"); - } else{ - this.Write(" output.Prediction = input.label.GetValues().ToArray().First();\r\n"); - } - this.Write("\r\n"); -if("MulticlassClassification".Equals(TaskType)){ - this.Write(" output.Score = input.probabilities.GetValues().ToArray();\r\n"); - } else { - this.Write(" output.Score = input.probabilities.GetValues().ToArray().First();\r\n"); - } - this.Write(@" } - // Factory method called when loading the model to get the mapping operation - public override Action GetMapping() - { - return Mapping; - } - } - public class LabelMappingInput - { - [ColumnName(""label"")] - public VBuffer label; - - [ColumnName(""probabilities"")] - public VBuffer probabilities; - } - public class LabelMappingOutput - { -"); -if("BinaryClassification".Equals(TaskType)){ - this.Write(" // ColumnName attribute is used to change the column name from\r\n\t\t// its " + - "default value, which is the name of the field\r\n [ColumnName(\"PredictedLab" + - "el\")]\r\n public bool Prediction { get; set; }\r\n"); - } if("MulticlassClassification".Equals(TaskType)){ - this.Write(" // ColumnName attribute is used to change the column name from\r\n\t\t// its " + - "default value, which is the name of the field\r\n [ColumnName(\"PredictedLab" + - "el\")]\r\n public "); - this.Write(this.ToStringHelper.ToStringWithCulture(PredictionLabelType)); - this.Write(" Prediction { get; set; }\r\n"); - } -if("MulticlassClassification".Equals(TaskType)){ - this.Write(" public float[] Score { get; set; }\r\n"); -}else{ - this.Write(" public float Score { get; set; }\r\n"); -} - this.Write(" }\r\n}\r\n"); - return this.GenerationEnvironment.ToString(); - } - -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -public string TaskType {get;set;} -public string PredictionLabelType {get;set;} -public string LabelMappingInputLabelType {get;set;} - - -void CLI_Annotation() -{ -this.Write(@"//***************************************************************************************** -//* * -//* This is an auto-generated file by Microsoft ML.NET CLI (Command-Line Interface) tool. * -//* * -//***************************************************************************************** -"); - - -} - - -void MB_Annotation() -{ -this.Write("// This file was auto-generated by ML.NET Model Builder. \r\n"); - - -} - - } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - internal class LabelMappingBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.tt b/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.tt deleted file mode 100644 index c17e4aaa03..0000000000 --- a/src/Microsoft.ML.CodeGenerator/Templates/Azure/Model/LabelMapping.tt +++ /dev/null @@ -1,77 +0,0 @@ -<#@ template language="C#" linePragmas = "false" visibility = "internal" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ include file="..\..\Console\Annotation.ttinclude" #><#if(Target == CSharp.GenerateTarget.Cli){ #> -<#CLI_Annotation();#> -<# } else if(Target == CSharp.GenerateTarget.ModelBuilder){ #> -<#MB_Annotation();#> -<# } #> - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace <#= Namespace #>.Model -{ - [CustomMappingFactoryAttribute(nameof(LabelMapping))] - public class LabelMapping : CustomMappingFactory - { - // Custom mapping to determine the label with the highest probability - public static void Mapping(LabelMappingInput input, LabelMappingOutput output) - { -<#if("Boolean".Equals(PredictionLabelType)){ #> - output.Prediction = input.label.GetValues().ToArray().First() == 1; -<# } else{ #> - output.Prediction = input.label.GetValues().ToArray().First(); -<# }#> - -<#if("MulticlassClassification".Equals(TaskType)){ #> - output.Score = input.probabilities.GetValues().ToArray(); -<# } else {#> - output.Score = input.probabilities.GetValues().ToArray().First(); -<# }#> - } - // Factory method called when loading the model to get the mapping operation - public override Action GetMapping() - { - return Mapping; - } - } - public class LabelMappingInput - { - [ColumnName("label")] - public VBuffer label; - - [ColumnName("probabilities")] - public VBuffer probabilities; - } - public class LabelMappingOutput - { -<#if("BinaryClassification".Equals(TaskType)){ #> - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field - [ColumnName("PredictedLabel")] - public bool Prediction { get; set; } -<# } if("MulticlassClassification".Equals(TaskType)){ #> - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field - [ColumnName("PredictedLabel")] - public <#= PredictionLabelType#> Prediction { get; set; } -<# }#> -<#if("MulticlassClassification".Equals(TaskType)){ #> - public float[] Score { get; set; } -<#}else{ #> - public float Score { get; set; } -<#}#> - } -} -<#+ -public string Namespace {get;set;} -internal CSharp.GenerateTarget Target {get;set;} -public string TaskType {get;set;} -public string PredictionLabelType {get;set;} -public string LabelMappingInputLabelType {get;set;} -#> \ No newline at end of file diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.LabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.LabelMapping.cs.approved.txt index 4763e479c5..e69de29bb2 100644 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.LabelMapping.cs.approved.txt +++ b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureCodeGeneratorTest.LabelMapping.cs.approved.txt @@ -1,42 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace Test.Model -{ - [CustomMappingFactoryAttribute(nameof(LabelMapping))] - public class LabelMapping : CustomMappingFactory - { - // Custom mapping to determine the label with the highest probability - public static void Mapping(LabelMappingInput input, LabelMappingOutput output) - { - output.Prediction = input.label.GetValues().ToArray().First() == 1; - - output.Score = input.probabilities.GetValues().ToArray(); - } - // Factory method called when loading the model to get the mapping operation - public override Action GetMapping() - { - return Mapping; - } - } - public class LabelMappingInput - { - [ColumnName("label")] - public VBuffer label; - - [ColumnName("probabilities")] - public VBuffer probabilities; - } - public class LabelMappingOutput - { - // ColumnName attribute is used to change the column name from - // its default value, which is the name of the field - [ColumnName("PredictedLabel")] - public Boolean Prediction { get; set; } - public float[] Score { get; set; } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.LabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.LabelMapping.cs.approved.txt deleted file mode 100644 index a6f05a499e..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.LabelMapping.cs.approved.txt +++ /dev/null @@ -1,48 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(LabelMapping))] - public class LabelMapping : CustomMappingFactory - { - public static string[] Label { get; set; } = new string[] { "label1", "label2", "label3", }; - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(LabelMappingInput input, LabelMappingOutput output) - { - var values = input.output1.GetValues().ToArray(); - var maxVal = values.Max(); - var exp = values.Select(v => Math.Exp(v - maxVal)); - var sumExp = exp.Sum(); - - exp.Select(v => (float)(v / sumExp)).ToArray(); - output.score = exp.Select(v => (float)(v / sumExp)).ToArray(); - - var maxValue = output.score.Max(); - var maxValueIndex = Array.IndexOf(output.score, maxValue); - output.label = Label[maxValueIndex]; - } - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - public class LabelMappingInput - { - [ColumnName("output1")] - public VBuffer output1; - } - public class LabelMappingOutput - { - - [ColumnName("PredictedLabel")] - public string label { get; set; } - - [ColumnName("Score")] - public float[] score { get; set; } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.NormalizeMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.NormalizeMapping.cs.approved.txt deleted file mode 100644 index 4687096197..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureImageCodeGeneratorTest.NormalizeMapping.cs.approved.txt +++ /dev/null @@ -1,50 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(NormalizeMapping))] - public class NormalizeMapping : CustomMappingFactory - { - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(NormalizeInput input, NormalizeOutput output) - { - var values = input.Reshape.GetValues().ToArray(); - - var image_mean = new float[] { 0.485f, 0.456f, 0.406f }; - var image_std = new float[] { 0.229f, 0.224f, 0.225f }; - - for (int x = 0; x < values.Count(); x++) - { - var y = x % 3; - // Normalize by 255 first - values[x] /= 255; - values[x] = (values[x] - image_mean[y]) / image_std[y]; - }; - - output.Reshape = new VBuffer(values.Count(), values); - } - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - public class NormalizeInput - { - [ColumnName("ImageSource_featurized")] - [VectorType(3, 224, 224)] - public VBuffer Reshape; - } - public class NormalizeOutput - { - [ColumnName("input1")] - [VectorType(3 * 224 * 224)] - public VBuffer Reshape; - } -} - diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt deleted file mode 100644 index d9645c8849..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.LabelMapping.cs.approved.txt +++ /dev/null @@ -1,104 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[] { "label1", "label2", "label3", }; - - public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) - { - long[] oldLabels = input.Labels.GetValues().ToArray(); - output.Labels = new string[oldLabels.Length]; - output.Scores = input.Scores.GetValues().ToArray(); - output.Boxes = input.Boxes.GetValues().ToArray(); - for (int i = 0; i < oldLabels.Length; i++) - { - output.Labels[i] = Label[oldLabels[i]]; - } - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class ObjectDetectionLabelMappingInput - { - [ColumnName("labels")] - public VBuffer Labels; - - [ColumnName("scores")] - public VBuffer Scores; - - [ColumnName("boxes")] - public VBuffer Boxes; - } - - public class BoundingBox - { - public float Top; - - public float Left; - - public float Right; - - public float Bottom; - - public string Label; - - public float Score; - - public override string ToString() - { - return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; - } - } - - - public class ModelOutput - { - [ColumnName("boxes")] - public float[] Boxes; - - [ColumnName("PredictedLabels")] - public string[] Labels; - - [ColumnName("scores")] - public float[] Scores; - - private BoundingBox[] BoundingBoxes - { - get - { - var boundingBoxes = new List(); - return boundingBoxes.ToArray(); - } - } - - public BoundingBox[] GetBoundingBoxes() - { - return this.BoundingBoxes; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var box in this.BoundingBoxes) - { - sb.AppendLine(box.ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt deleted file mode 100644 index 4687096197..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.NormalizeMapping.cs.approved.txt +++ /dev/null @@ -1,50 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(NormalizeMapping))] - public class NormalizeMapping : CustomMappingFactory - { - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(NormalizeInput input, NormalizeOutput output) - { - var values = input.Reshape.GetValues().ToArray(); - - var image_mean = new float[] { 0.485f, 0.456f, 0.406f }; - var image_std = new float[] { 0.229f, 0.224f, 0.225f }; - - for (int x = 0; x < values.Count(); x++) - { - var y = x % 3; - // Normalize by 255 first - values[x] /= 255; - values[x] = (values[x] - image_mean[y]) / image_std[y]; - }; - - output.Reshape = new VBuffer(values.Count(), values); - } - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - public class NormalizeInput - { - [ColumnName("ImageSource_featurized")] - [VectorType(3, 224, 224)] - public VBuffer Reshape; - } - public class NormalizeOutput - { - [ColumnName("input1")] - [VectorType(3 * 224 * 224)] - public VBuffer Reshape; - } -} - diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt deleted file mode 100644 index 1f56207e5f..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionLabelMapping.cs.approved.txt +++ /dev/null @@ -1,87 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[] { "label1", "label2", "label3", }; - - public static void Mapping(ObjectDetectionLabelMappingInput input, ObjectDetectionLabelMappingOutput output) - { - long[] oldLabels = input.Labels.GetValues().ToArray(); - output.Labels = new string[oldLabels.Length]; - output.Scores = input.Scores.GetValues().ToArray(); - output.Boxes = input.Boxes.GetValues().ToArray(); - for (int i = 0; i < oldLabels.Length; i++) - { - output.Labels[i] = Label[oldLabels[i]]; - } - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class ObjectDetectionLabelMappingInput - { - [ColumnName("labels")] - public VBuffer Labels; - - [ColumnName("scores")] - public VBuffer Scores; - - [ColumnName("boxes")] - public VBuffer Boxes; - } - - - - public class ObjectDetectionLabelMappingOutput - { - [ColumnName("boxes")] - public float[] Boxes; - - [ColumnName("PredictedLabels")] - public string[] Labels; - - [ColumnName("scores")] - public float[] Scores; - - private BoundingBox[] BoundingBoxes - { - get - { - var boundingBoxes = new List(); - return boundingBoxes.ToArray(); - } - } - - public BoundingBox[] GetBoundingBoxes() - { - return this.BoundingBoxes; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var box in this.BoundingBoxes) - { - sb.AppendLine(box.ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt deleted file mode 100644 index 4208123483..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.approved.txt +++ /dev/null @@ -1,104 +0,0 @@ - // This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[] { "label1", "label2", "label3", }; - - public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) - { - long[] oldLabels = input.Labels.GetValues().ToArray(); - output.Labels = new string[oldLabels.Length]; - output.Scores = input.Scores.GetValues().ToArray(); - output.Boxes = input.Boxes.GetValues().ToArray(); - for (int i = 0; i < oldLabels.Length; i++) - { - output.Labels[i] = Label[oldLabels[i]]; - } - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class ObjectDetectionLabelMappingInput - { - [ColumnName("labels")] - public VBuffer Labels; - - [ColumnName("scores")] - public VBuffer Scores; - - [ColumnName("boxes")] - public VBuffer Boxes; - } - - public class BoundingBox - { - public float Top; - - public float Left; - - public float Right; - - public float Bottom; - - public string Label; - - public float Score; - - public override string ToString() - { - return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; - } - } - - - public class ObjectDetectionLabelMappingOutput - { - [ColumnName("boxes")] - public float[] Boxes; - - [ColumnName("PredictedLabels")] - public string[] Labels; - - [ColumnName("scores")] - public float[] Scores; - - private BoundingBox[] BoundingBoxes - { - get - { - var boundingBoxes = new List(); - return boundingBoxes.ToArray(); - } - } - - public BoundingBox[] GetBoundingBoxes() - { - return this.BoundingBoxes; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var box in this.BoundingBoxes) - { - sb.AppendLine(box.ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt deleted file mode 100644 index 862aa53ab8..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ObjectDetectionOnnxMapping.cs.received.txt +++ /dev/null @@ -1,104 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; -using System; -using System.Linq; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(ObjectDetectionLabelMapping))] - public class ObjectDetectionLabelMapping : CustomMappingFactory - { - public static string[] Label = new string[] { "label1", "label2", "label3", }; - - public static void Mapping(ObjectDetectionLabelMappingInput input, ModelOutput output) - { - long[] oldLabels = input.Labels.GetValues().ToArray(); - output.Labels = new string[oldLabels.Length]; - output.Scores = input.Scores.GetValues().ToArray(); - output.Boxes = input.Boxes.GetValues().ToArray(); - for (int i = 0; i < oldLabels.Length; i++) - { - output.Labels[i] = Label[oldLabels[i]]; - } - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class ObjectDetectionLabelMappingInput - { - [ColumnName("labels")] - public VBuffer Labels; - - [ColumnName("scores")] - public VBuffer Scores; - - [ColumnName("boxes")] - public VBuffer Boxes; - } - - public class BoundingBox - { - public float Top; - - public float Left; - - public float Right; - - public float Bottom; - - public string Label; - - public float Score; - - public override string ToString() - { - return $"Top: {this.Top}, Left: {this.Left}, Right: {this.Right}, Bottom: {this.Bottom}, Label: {this.Label}, Score: {this.Score}"; - } - } - - - public class ObjectDetectionLabelMappingOutput - { - [ColumnName("boxes")] - public float[] Boxes; - - [ColumnName("PredictedLabels")] - public string[] Labels; - - [ColumnName("scores")] - public float[] Scores; - - private BoundingBox[] BoundingBoxes - { - get - { - var boundingBoxes = new List(); - return boundingBoxes.ToArray(); - } - } - - public BoundingBox[] GetBoundingBoxes() - { - return this.BoundingBoxes; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var box in this.BoundingBoxes) - { - sb.AppendLine(box.ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt b/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt deleted file mode 100644 index d1b48b85b9..0000000000 --- a/test/Microsoft.ML.CodeGenerator.Tests/ApprovalTests/ConsoleCodeGeneratorTests.AzureObjectDetectionCodeGeneratorTest.ReshapeTransformer.cs.approved.txt +++ /dev/null @@ -1,48 +0,0 @@ -// This file was auto-generated by ML.NET Model Builder. - -using System; -using System.Linq; -using Microsoft.ML.Data; -using Microsoft.ML.Transforms; - -namespace CodeGenTest.Model -{ - [CustomMappingFactoryAttribute(nameof(ReshapeTransformer))] - public class ReshapeTransformer : CustomMappingFactory - { - // This is the custom mapping. We now separate it into a method, so that we can use it both in training and in loading. - public static void Mapping(Input input, Output output) - { - var values = input.Reshape.GetValues().ToArray(); - - for (int x = 0; x < values.Count(); x++) - { - // normalize from [0, 255] to [0, 1] - values[x] /= 255; - }; - - output.Reshape = new VBuffer(values.Count(), values); - } - - // This factory method will be called when loading the model to get the mapping operation. - public override Action GetMapping() - { - return Mapping; - } - } - - public class Input - { - [ColumnName("input")] - [VectorType(3, 800, 600)] - public VBuffer Reshape; - } - - public class Output - { - [ColumnName("input")] - [VectorType(1 * 3 * 600 * 800)] - public VBuffer Reshape; - } -} -