/// <summary>
 /// Constructor: model location relative to .exe, mlcontext, model settings of input name and output name
 /// </summary>
 /// <param name="inputModelLocation"></param>
 /// <param name="inputMlContext"></param>
 /// <param name="inputModelSettings"></param>
 public OnnxRNNScorer(string inputModelLocation, MLContext inputMlContext, ModelSettings inputModelSettings)
 {
     modelLocation = inputModelLocation;
     mlContext     = inputMlContext;
     modelSettings = inputModelSettings;
     pipeline      = mlContext.Transforms.ApplyOnnxModel(modelFile: modelLocation, outputColumnNames: new[] { modelSettings.modelOutput }, inputColumnNames: new[] { modelSettings.modelInput });
 }
예제 #2
0
        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
        public void TestUnknownDimensions()
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return;
            }

            // model contains -1 in input and output shape dimensions
            // model: input dims = [-1, 3], output argmax dims = [-1]
            var modelFile = @"unknowndimensions/test_unknowndimensions_float.onnx";
            var mlContext = new MLContext();
            var data      = new TestDataUnknownDimensions[]
            {
                new TestDataUnknownDimensions()
                {
                    input = new float[] { 1.1f, 1.3f, 1.2f }
                },
                new TestDataUnknownDimensions()
                {
                    input = new float[] { -1.1f, -1.3f, -1.2f }
                },
                new TestDataUnknownDimensions()
                {
                    input = new float[] { -1.1f, -1.3f, 1.2f }
                },
            };
            var idv               = mlContext.CreateStreamingDataView(data);
            var pipeline          = new OnnxScoringEstimator(mlContext, modelFile);
            var transformedValues = pipeline.Fit(idv).Transform(idv);
            var predictions       = transformedValues.AsEnumerable <PredictionUnknownDimensions>(mlContext, reuseRowObject: false).ToArray();

            Assert.Equal(1, predictions[0].argmax[0]);
            Assert.Equal(0, predictions[1].argmax[0]);
            Assert.Equal(2, predictions[2].argmax[0]);
        }
예제 #3
0
        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 fails with "An attempt was made to load a program with an incorrect format."
        void TestSimpleCase()
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return;
            }

            var modelFile = "squeezenet/00000001/model.onnx";

            var samplevector = GetSampleArrayData();

            var dataView = ComponentCreation.CreateDataView(Env,
                                                            new TestData[] {
                new TestData()
                {
                    data_0 = samplevector
                },
                new TestData()
                {
                    data_0 = samplevector
                }
            });

            var xyData = new List <TestDataXY> {
                new TestDataXY()
                {
                    A = new float[inputSize]
                }
            };
            var stringData = new List <TestDataDifferntType> {
                new TestDataDifferntType()
                {
                    data_0 = new string[inputSize]
                }
            };
            var sizeData = new List <TestDataSize> {
                new TestDataSize()
                {
                    data_0 = new float[2]
                }
            };
            var pipe = new OnnxScoringEstimator(Env, modelFile, new[] { "data_0" }, new[] { "softmaxout_1" });

            var invalidDataWrongNames      = ComponentCreation.CreateDataView(Env, xyData);
            var invalidDataWrongTypes      = ComponentCreation.CreateDataView(Env, stringData);
            var invalidDataWrongVectorSize = ComponentCreation.CreateDataView(Env, sizeData);

            TestEstimatorCore(pipe, dataView, invalidInput: invalidDataWrongNames);
            TestEstimatorCore(pipe, dataView, invalidInput: invalidDataWrongTypes);

            pipe.GetOutputSchema(SchemaShape.Create(invalidDataWrongVectorSize.Schema));
            try
            {
                pipe.Fit(invalidDataWrongVectorSize);
                Assert.False(true);
            }
            catch (ArgumentOutOfRangeException) { }
            catch (InvalidOperationException) { }
        }
예제 #4
0
        /// <summary>
        /// Example use of OnnxEstimator in an ML.NET pipeline
        /// </summary>
        public static void OnnxTransformSample()
        {
            // Download the squeeznet image model from ONNX model zoo, version 1.2
            // https://github.com/onnx/models/tree/master/squeezenet
            var modelPath = @"squeezenet\model.onnx";

            // Inspect the model's inputs and outputs
            var session    = new InferenceSession(modelPath);
            var inputInfo  = session.InputMetadata.First();
            var outputInfo = session.OutputMetadata.First();

            Console.WriteLine($"Input Name is {String.Join(",", inputInfo.Key)}");
            Console.WriteLine($"Input Dimensions are {String.Join(",", inputInfo.Value.Dimensions)}");
            Console.WriteLine($"Output Name is {String.Join(",", outputInfo.Key)}");
            Console.WriteLine($"Output Dimensions are {String.Join(",", outputInfo.Value.Dimensions)}");
            // Results..
            // Input Name is data_0
            // Input Dimensions are 1,3,224,224
            // Output Name is softmaxout_1
            // Output Dimensions are 1,1000,1,1

            // Create ML pipeline to score the data using OnnxScoringEstimator
            var mlContext = new MLContext();
            var data      = GetTensorData();
            var idv       = mlContext.Data.ReadFromEnumerable(data);
            var pipeline  = new OnnxScoringEstimator(mlContext, new[] { outputInfo.Key }, new[] { inputInfo.Key }, modelPath);

            // Run the pipeline and get the transformed values
            var transformedValues = pipeline.Fit(idv).Transform(idv);

            // Retrieve model scores into Prediction class
            var predictions = mlContext.CreateEnumerable <Prediction>(transformedValues, reuseRowObject: false);

            // Iterate rows
            foreach (var prediction in predictions)
            {
                int numClasses = 0;
                foreach (var classScore in prediction.softmaxout_1.Take(3))
                {
                    Console.WriteLine($"Class #{numClasses++} score = {classScore}");
                }
                Console.WriteLine(new string('-', 10));
            }

            // Results look like below...
            // Class #0 score = 4.544065E-05
            // Class #1 score = 0.003845858
            // Class #2 score = 0.0001249467
            // ----------
            // Class #0 score = 4.491953E-05
            // Class #1 score = 0.003848222
            // Class #2 score = 0.0001245592
            // ----------
        }
예제 #5
0
        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30AndNotFullFramework))] // Tracked by https://github.com/dotnet/machinelearning/issues/2087
        public void KmeansOnnxConversionTest()
        {
            // Create a new context for ML.NET operations. It can be used for exception tracking and logging,
            // as a catalog of available operations and as the source of randomness.
            var mlContext = new MLContext(seed: 1, conc: 1);

            string dataPath = GetDataPath("breast-cancer.txt");
            // Now read the file (remember though, readers are lazy, so the actual reading will happen when the data is accessed).
            var data = mlContext.Data.ReadFromTextFile <BreastCancerFeatureVector>(dataPath,
                                                                                   hasHeader: true,
                                                                                   separatorChar: '\t');

            var pipeline = mlContext.Transforms.Normalize("Features").
                           Append(mlContext.Clustering.Trainers.KMeans(new Trainers.KMeans.KMeansPlusPlusTrainer.Options
            {
                FeatureColumn = DefaultColumnNames.Features,
                MaxIterations = 1,
                ClustersCount = 4,
                NumThreads    = 1,
                InitAlgorithm = Trainers.KMeans.KMeansPlusPlusTrainer.InitAlgorithm.Random
            }));

            var model           = pipeline.Fit(data);
            var transformedData = model.Transform(data);

            var onnxModel = mlContext.Model.ConvertToOnnx(model, data);

            // Compare results produced by ML.NET and ONNX's runtime.
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.Is64BitProcess)
            {
                var onnxFileName  = "model.onnx";
                var onnxModelPath = GetOutputPath(onnxFileName);
                SaveOnnxModel(onnxModel, onnxModelPath, null);

                // Evaluate the saved ONNX model using the data used to train the ML.NET pipeline.
                string[] inputNames      = onnxModel.Graph.Input.Select(valueInfoProto => valueInfoProto.Name).ToArray();
                string[] outputNames     = onnxModel.Graph.Output.Select(valueInfoProto => valueInfoProto.Name).ToArray();
                var      onnxEstimator   = new OnnxScoringEstimator(mlContext, onnxModelPath, inputNames, outputNames);
                var      onnxTransformer = onnxEstimator.Fit(data);
                var      onnxResult      = onnxTransformer.Transform(data);
                CompareSelectedR4VectorColumns("Score", "Score0", transformedData, onnxResult, 3);
            }

            // Check ONNX model's text format. We save the produced ONNX model as a text file and compare it against
            // the associated file in ML.NET repo. Such a comparison can be retired if ONNXRuntime ported to ML.NET
            // can support Linux and Mac.
            var subDir       = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "Cluster", "BreastCancer");
            var onnxTextName = "Kmeans.txt";
            var onnxTextPath = GetOutputPath(subDir, onnxTextName);

            SaveOnnxModel(onnxModel, null, onnxTextPath);
            CheckEquality(subDir, onnxTextName, digitsOfPrecision: 2);
            Done();
        }
        void TestSimpleCase()
        {
            var modelFile    = "squeezenet/00000001/model.onnx";
            var samplevector = GetSampleArrayData();
            var dataView     = ML.Data.ReadFromEnumerable(
                new TestData[] {
                new TestData()
                {
                    data_0 = samplevector
                },
                new TestData()
                {
                    data_0 = samplevector
                }
            });

            var xyData = new List <TestDataXY> {
                new TestDataXY()
                {
                    A = new float[inputSize]
                }
            };
            var stringData = new List <TestDataDifferntType> {
                new TestDataDifferntType()
                {
                    data_0 = new string[inputSize]
                }
            };
            var sizeData = new List <TestDataSize> {
                new TestDataSize()
                {
                    data_0 = new float[2]
                }
            };
            var pipe = new OnnxScoringEstimator(Env, new[] { "softmaxout_1" }, new[] { "data_0" }, modelFile);

            var invalidDataWrongNames      = ML.Data.ReadFromEnumerable(xyData);
            var invalidDataWrongTypes      = ML.Data.ReadFromEnumerable(stringData);
            var invalidDataWrongVectorSize = ML.Data.ReadFromEnumerable(sizeData);

            TestEstimatorCore(pipe, dataView, invalidInput: invalidDataWrongNames);
            TestEstimatorCore(pipe, dataView, invalidInput: invalidDataWrongTypes);

            pipe.GetOutputSchema(SchemaShape.Create(invalidDataWrongVectorSize.Schema));
            try
            {
                pipe.Fit(invalidDataWrongVectorSize);
                Assert.False(true);
            }
            catch (ArgumentOutOfRangeException) { }
            catch (InvalidOperationException) { }
        }
예제 #7
0
        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.NotFullFramework))] // Tracked by https://github.com/dotnet/machinelearning/issues/2106
        public void SimpleEndToEndOnnxConversionTest()
        {
            // Step 1: Create and train a ML.NET pipeline.
            var trainDataPath = GetDataPath(TestDatasets.generatedRegressionDataset.trainFilename);
            var mlContext     = new MLContext(seed: 1, conc: 1);
            var data          = mlContext.Data.ReadFromTextFile <AdultData>(trainDataPath,
                                                                            hasHeader: true,
                                                                            separatorChar: ';'
                                                                            );
            var cachedTrainData = mlContext.Data.Cache(data);
            var dynamicPipeline =
                mlContext.Transforms.Normalize("FeatureVector")
                .AppendCacheCheckpoint(mlContext)
                .Append(mlContext.Regression.Trainers.StochasticDualCoordinateAscent(labelColumn: "Target", featureColumn: "FeatureVector"));
            var model           = dynamicPipeline.Fit(data);
            var transformedData = model.Transform(data);

            // Step 2: Convert ML.NET model to ONNX format and save it as a file.
            var onnxModel     = mlContext.Model.ConvertToOnnx(model, data);
            var onnxFileName  = "model.onnx";
            var onnxModelPath = GetOutputPath(onnxFileName);

            SaveOnnxModel(onnxModel, onnxModelPath, null);

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.Is64BitProcess)
            {
                // Step 3: Evaluate the saved ONNX model using the data used to train the ML.NET pipeline.
                string[] inputNames      = onnxModel.Graph.Input.Select(valueInfoProto => valueInfoProto.Name).ToArray();
                string[] outputNames     = onnxModel.Graph.Output.Select(valueInfoProto => valueInfoProto.Name).ToArray();
                var      onnxEstimator   = new OnnxScoringEstimator(mlContext, onnxModelPath, inputNames, outputNames);
                var      onnxTransformer = onnxEstimator.Fit(data);
                var      onnxResult      = onnxTransformer.Transform(data);

                // Step 4: Compare ONNX and ML.NET results.
                CompareSelectedR4ScalarColumns("Score", "Score0", transformedData, onnxResult, 1);
            }

            // Step 5: Check ONNX model's text format. This test will be not necessary if Step 3 and Step 4 can run on Linux and
            // Mac to support cross-platform tests.
            var subDir       = Path.Combine("..", "..", "BaselineOutput", "Common", "Onnx", "Regression", "Adult");
            var onnxTextName = "SimplePipeline.txt";
            var onnxTextPath = GetOutputPath(subDir, onnxTextName);

            SaveOnnxModel(onnxModel, null, onnxTextPath);
            CheckEquality(subDir, onnxTextName, digitsOfPrecision: 3);

            Done();
        }
예제 #8
0
        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 fails with "An attempt was made to load a program with an incorrect format."
        void TestOldSavingAndLoading()
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return;
            }

            var modelFile = "squeezenet/00000001/model.onnx";

            var samplevector = GetSampleArrayData();

            var dataView = ComponentCreation.CreateDataView(Env,
                                                            new TestData[] {
                new TestData()
                {
                    data_0 = samplevector
                }
            });

            var inputNames  = new[] { "data_0" };
            var outputNames = new[] { "softmaxout_1" };
            var est         = new OnnxScoringEstimator(Env, modelFile, inputNames, outputNames);
            var transformer = est.Fit(dataView);
            var result      = transformer.Transform(dataView);
            var resultRoles = new RoleMappedData(result);

            using (var ms = new MemoryStream())
            {
                TrainUtils.SaveModel(Env, Env.Start("saving"), ms, null, resultRoles);
                ms.Position = 0;
                var loadedView = ModelFileUtils.LoadTransforms(Env, dataView, ms);

                loadedView.Schema.TryGetColumnIndex(outputNames[0], out int softMaxOut1);
                using (var cursor = loadedView.GetRowCursor(col => col == softMaxOut1))
                {
                    VBuffer <float> softMaxValue  = default;
                    var             softMaxGetter = cursor.GetGetter <VBuffer <float> >(softMaxOut1);
                    float           sum           = 0f;
                    int             i             = 0;
                    while (cursor.MoveNext())
                    {
                        softMaxGetter(ref softMaxValue);
                        var values = softMaxValue.DenseValues();
                        foreach (var val in values)
                        {
                            sum += val;
                            if (i == 0)
                            {
                                Assert.InRange(val, 0.00004, 0.00005);
                            }
                            if (i == 1)
                            {
                                Assert.InRange(val, 0.003844, 0.003845);
                            }
                            if (i == 999)
                            {
                                Assert.InRange(val, 0.0029566, 0.0029567);
                            }
                            i++;
                        }
                    }
                    Assert.InRange(sum, 1.0, 1.00001);
                }
            }
        }
예제 #9
0
        public void TestOnnxTransformWithCustomShapes()
        {
            // The loaded model has input shape [-1, 3] and output shape [-1].
            var modelFile = Path.Combine(Directory.GetCurrentDirectory(), "unknowndimensions", "test_unknowndimensions_float.onnx");

            var dataPoints = new InputWithCustomShape[]
            {
                // It's a flattened 3-by-3 tensor.
                // [1.1, 1.3, 1.2]
                // |1.9, 1.3, 1.2|
                // [1.1, 1.3, 1.8]
                new InputWithCustomShape()
                {
                    input = new float[] { 1.1f, 1.3f, 1.2f, 1.9f, 1.3f, 1.2f, 1.1f, 1.3f, 1.8f }
                },
                // It's a flattened 3-by-3 tensor.
                // [0, 0, 1]
                // |1, 0, 0|
                // [1, 0, 0]
                new InputWithCustomShape()
                {
                    input = new float[] { 0f, 0f, 1f, 1f, 0f, 0f, 1f, 0f, 0f }
                }
            };

            var shapeDictionary = new Dictionary <string, int[]>()
            {
                { nameof(InputWithCustomShape.input), new int[] { 3, 3 } }
            };

            var dataView = ML.Data.LoadFromEnumerable(dataPoints);

            var pipeline             = new OnnxScoringEstimator[3];
            var onnxTransformer      = new OnnxTransformer[3];
            var transformedDataViews = new IDataView[3];

            // Test three public ONNX APIs with the custom shape.

            // Test 1.
            pipeline[0] = ML.Transforms.ApplyOnnxModel(
                new[] { nameof(PredictionWithCustomShape.argmax) }, new[] { nameof(InputWithCustomShape.input) },
                modelFile, shapeDictionary);
            onnxTransformer[0]      = pipeline[0].Fit(dataView);
            transformedDataViews[0] = onnxTransformer[0].Transform(dataView);

            // Test 2.
            pipeline[1] = ML.Transforms.ApplyOnnxModel(
                nameof(PredictionWithCustomShape.argmax), nameof(InputWithCustomShape.input),
                modelFile, shapeDictionary);
            onnxTransformer[1]      = pipeline[1].Fit(dataView);
            transformedDataViews[1] = onnxTransformer[1].Transform(dataView);

            // Test 3.
            pipeline[2]             = ML.Transforms.ApplyOnnxModel(modelFile, shapeDictionary);
            onnxTransformer[2]      = pipeline[2].Fit(dataView);
            transformedDataViews[2] = onnxTransformer[2].Transform(dataView);

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

                // One data point generates one transformed data point.
                Assert.Equal(dataPoints.Count(), transformedDataPoints.Count);

                // Check result numbers. They are results of applying ONNX argmax along the second axis; for example
                // [1.1, 1.3, 1.2] ---> [1] because 1.3 (indexed by 1) is the largest element.
                // |1.9, 1.3, 1.2| ---> |0|         1.9             0
                // [1.1, 1.3, 1.8] ---> [2]         1.8             2
                var expectedResults = new long[][]
                {
                    new long[] { 1, 0, 2 },
                    new long[] { 2, 0, 0 }
                };

                for (int i = 0; i < transformedDataPoints.Count; ++i)
                {
                    Assert.Equal(transformedDataPoints[i].argmax, expectedResults[i]);
                }
            }
            for (int i = 0; i < 3; i++)
            {
                (onnxTransformer[i] as IDisposable)?.Dispose();
            }
        }