From 1ca84f100f2181ee377135b7ba9dbcf1862a62a1 Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Mon, 25 Jul 2022 17:15:11 -0700 Subject: [PATCH 1/3] add dnn featurizer --- src/Microsoft.ML.AutoML/API/AutoCatalog.cs | 58 ++++++++++++++++- .../dnn_featurizer_image_search_space.json | 22 +++++++ .../CodeGen/estimator-schema.json | 17 ++--- .../CodeGen/search-space-schema.json | 53 +++++++++++----- .../CodeGen/transformer-estimators.json | 21 +++++++ .../Microsoft.ML.AutoML.csproj | 1 + .../SweepableEstimator/Estimators/Images.cs | 26 +++++++- .../SearchSpaceGenerator.cs | 62 +++++++++---------- 8 files changed, 198 insertions(+), 62 deletions(-) create mode 100644 src/Microsoft.ML.AutoML/CodeGen/dnn_featurizer_image_search_space.json diff --git a/src/Microsoft.ML.AutoML/API/AutoCatalog.cs b/src/Microsoft.ML.AutoML/API/AutoCatalog.cs index 0c3f3ab222..9e63a004bf 100644 --- a/src/Microsoft.ML.AutoML/API/AutoCatalog.cs +++ b/src/Microsoft.ML.AutoML/API/AutoCatalog.cs @@ -587,6 +587,44 @@ internal SweepableEstimator[] CatalogFeaturizer(string[] outputColumnNames, stri return new SweepableEstimator[] { SweepableEstimatorFactory.CreateOneHotEncoding(option), SweepableEstimatorFactory.CreateOneHotHashEncoding(option) }; } + internal MultiModelPipeline ImagePathFeaturizer(string outputColumnName, string inputColumnName) + { + // load image => resize image (224, 224) => extract pixels => dnn featurizer + var loadImageOption = new LoadImageOption + { + ImageFolder = null, + InputColumnName = inputColumnName, + OutputColumnName = outputColumnName, + }; + + var resizeImageOption = new ResizeImageOption + { + ImageHeight = 224, + ImageWidth = 224, + InputColumnName = inputColumnName, + OutputColumnName = outputColumnName, + }; + + var extractPixelOption = new ExtractPixelsOption + { + InputColumnName = inputColumnName, + OutputColumnName = outputColumnName, + }; + + var dnnFeaturizerOption = new DnnFeaturizerImageOption + { + InputColumnName = inputColumnName, + OutputColumnName = outputColumnName, + }; + + var pipeline = new MultiModelPipeline(); + + return pipeline.Append(SweepableEstimatorFactory.CreateLoadImages(loadImageOption)) + .Append(SweepableEstimatorFactory.CreateResizeImages(resizeImageOption)) + .Append(SweepableEstimatorFactory.CreateExtractPixels(extractPixelOption)) + .Append(SweepableEstimatorFactory.CreateDnnFeaturizerImage(dnnFeaturizerOption)); + } + /// /// Create a single featurize pipeline according to . This function will collect all columns in and not in , /// featurizing them using , or . And combine @@ -596,9 +634,10 @@ internal SweepableEstimator[] CatalogFeaturizer(string[] outputColumnNames, stri /// columns that should be treated as catalog. If not specified, it will automatically infer if a column is catalog or not. /// columns that should be treated as numeric. If not specified, it will automatically infer if a column is catalog or not. /// columns that should be treated as text. If not specified, it will automatically infer if a column is catalog or not. + /// columns that should be treated as image path. If not specified, it will automatically infer if a column is catalog or not. /// output feature column. /// columns that won't be included when featurizing, like label - public MultiModelPipeline Featurizer(IDataView data, string outputColumnName = "Features", string[] catalogColumns = null, string[] numericColumns = null, string[] textColumns = null, string[] excludeColumns = null) + public MultiModelPipeline Featurizer(IDataView data, string outputColumnName = "Features", string[] catalogColumns = null, string[] numericColumns = null, string[] textColumns = null, string[] imagePathColumns = null, string[] excludeColumns = null) { Contracts.CheckValue(data, nameof(data)); @@ -646,6 +685,14 @@ public MultiModelPipeline Featurizer(IDataView data, string outputColumnName = " } } + if (imagePathColumns != null) + { + foreach (var column in imagePathColumns) + { + columnInfo.ImagePathColumnNames.Add(column); + } + } + return this.Featurizer(data, columnInfo, outputColumnName); } @@ -667,9 +714,11 @@ public MultiModelPipeline Featurizer(IDataView data, ColumnInformation columnInf var textFeatures = columnPurposes.Where(c => c.Purpose == ColumnPurpose.TextFeature); var numericFeatures = columnPurposes.Where(c => c.Purpose == ColumnPurpose.NumericFeature); var catalogFeatures = columnPurposes.Where(c => c.Purpose == ColumnPurpose.CategoricalFeature); + var imagePathFeatures = columnPurposes.Where(c => c.Purpose == ColumnPurpose.ImagePath); var textFeatureColumnNames = textFeatures.Select(c => data.Schema[c.ColumnIndex].Name).ToArray(); var numericFeatureColumnNames = numericFeatures.Select(c => data.Schema[c.ColumnIndex].Name).ToArray(); var catalogFeatureColumnNames = catalogFeatures.Select(c => data.Schema[c.ColumnIndex].Name).ToArray(); + var imagePathColumnNames = imagePathFeatures.Select(c => data.Schema[c.ColumnIndex].Name).ToArray(); var pipeline = new MultiModelPipeline(); if (numericFeatureColumnNames.Length > 0) @@ -682,6 +731,11 @@ public MultiModelPipeline Featurizer(IDataView data, ColumnInformation columnInf pipeline = pipeline.Append(this.CatalogFeaturizer(catalogFeatureColumnNames, catalogFeatureColumnNames)); } + foreach (var imagePathColumn in imagePathColumnNames) + { + pipeline = pipeline.Append(this.ImagePathFeaturizer(imagePathColumn, imagePathColumn)); + } + foreach (var textColumn in textFeatureColumnNames) { pipeline = pipeline.Append(this.TextFeaturizer(textColumn, textColumn)); @@ -689,7 +743,7 @@ public MultiModelPipeline Featurizer(IDataView data, ColumnInformation columnInf var option = new ConcatOption { - InputColumnNames = textFeatureColumnNames.Concat(numericFeatureColumnNames).Concat(catalogFeatureColumnNames).ToArray(), + InputColumnNames = textFeatureColumnNames.Concat(numericFeatureColumnNames).Concat(catalogFeatureColumnNames).Concat(imagePathColumnNames).ToArray(), OutputColumnName = outputColumnName, }; diff --git a/src/Microsoft.ML.AutoML/CodeGen/dnn_featurizer_image_search_space.json b/src/Microsoft.ML.AutoML/CodeGen/dnn_featurizer_image_search_space.json new file mode 100644 index 0000000000..2b7136d513 --- /dev/null +++ b/src/Microsoft.ML.AutoML/CodeGen/dnn_featurizer_image_search_space.json @@ -0,0 +1,22 @@ +{ + "$schema": "./search-space-schema.json#", + "name": "dnn_featurizer_image_option", + "search_space": [ + { + "name": "OutputColumnName", + "type": "string" + }, + { + "name": "InputColumnName", + "type": "string" + }, + { + "name": "ModelFactory", + "type": "dnnModelFactory", + "default": "resnet_18", + "search_space": [ + "alexnet", "resnet_101", "resnet_18", "resnet_50" + ] + } + ] +} diff --git a/src/Microsoft.ML.AutoML/CodeGen/estimator-schema.json b/src/Microsoft.ML.AutoML/CodeGen/estimator-schema.json index 4467ec810c..ce39ff8887 100644 --- a/src/Microsoft.ML.AutoML/CodeGen/estimator-schema.json +++ b/src/Microsoft.ML.AutoML/CodeGen/estimator-schema.json @@ -68,6 +68,7 @@ "ApplyOnnxModel", "ResizeImages", "ExtractPixels", + "DnnFeaturizerImage", "Naive", "ForecastBySsa" ] @@ -180,22 +181,12 @@ "confidenceLowerBoundColumn", "confidenceUpperBoundColumn", "confidenceLevel", - "variableHorizon" + "variableHorizon", + "modelFactory" ] }, "argumentType": { - "type": "string", - "enum": [ - "integer", - "float", - "double", - "string", - "boolean", - "resizingKind", - "colorBits", - "colorsOrder", - "anchor" - ] + "$ref": "search-space-schema.json#/definitions/option_type" } } } diff --git a/src/Microsoft.ML.AutoML/CodeGen/search-space-schema.json b/src/Microsoft.ML.AutoML/CodeGen/search-space-schema.json index 224ea9a375..f99c390b62 100644 --- a/src/Microsoft.ML.AutoML/CodeGen/search-space-schema.json +++ b/src/Microsoft.ML.AutoML/CodeGen/search-space-schema.json @@ -2,6 +2,29 @@ "$schema": "http://json-schema.org/draft-04/schema", "title": "Search Space", "definitions": { + "boolArray": { + "type": "array", + "items": { "type": "boolean" } + }, + "intArray": { + "type": "array", + "items": { "type": "integer" } + }, + "dnnModelFactoryArray": { + "type": "array", + "items": { + "$ref": "#/definitions/dnnModelFactoryType" + } + }, + "dnnModelFactoryType": { + "type": "string", + "enum": [ + "resnet_18", + "resnet_50", + "resnet_101", + "alexnet" + ] + }, "range": { "type": "object", "properties": { @@ -12,18 +35,17 @@ "required": [ "min", "max" ] }, "choice": { - "type": "object", - "properties": { - "value": { - "oneOf": [ - { "type": "string" }, - { "type": "number" }, - { "type": "integer" }, - { "type": "boolean" } - ] + "oneOf": [ + { + "$ref": "#/definitions/intArray" + }, + { + "$ref": "#/definitions/dnnModelFactoryArray" + }, + { + "$ref": "#/definitions/boolArray" } - }, - "required": [ "value" ] + ] }, "option": { "type": "object", @@ -86,7 +108,8 @@ "extract_pixels_option", "load_image_option", "image_classification_option", - "matrix_factorization_option" + "matrix_factorization_option", + "dnn_featurizer_image_option" ] }, "option_name": { @@ -130,7 +153,8 @@ "ApproximationRank", "NumberOfIterations", "Quiet", - "OutputAsFloatArray" + "OutputAsFloatArray", + "ModelFactory" ] }, "option_type": { @@ -145,7 +169,8 @@ "resizingKind", "colorBits", "colorsOrder", - "anchor" + "anchor", + "dnnModelFactory" ] } }, diff --git a/src/Microsoft.ML.AutoML/CodeGen/transformer-estimators.json b/src/Microsoft.ML.AutoML/CodeGen/transformer-estimators.json index 88d3e94adc..0fc42d3aa6 100644 --- a/src/Microsoft.ML.AutoML/CodeGen/transformer-estimators.json +++ b/src/Microsoft.ML.AutoML/CodeGen/transformer-estimators.json @@ -321,6 +321,27 @@ "nugetDependencies": [ "Microsoft.ML", "Microsoft.ML.ImageAnalytics" ], "usingStatements": [ "Microsoft.ML" ], "searchOption": "load_image_option" + }, + { + "functionName": "DnnFeaturizerImage", + "estimatorTypes": [ "Transforms" ], + "arguments": [ + { + "argumentName": "outputColumnName", + "argumentType": "string" + }, + { + "argumentName": "inputColumnName", + "argumentType": "string" + }, + { + "argumentName": "modelFactory", + "argumentType": "dnnModelFactory" + } + ], + "nugetDependencies": [ "Microsoft.ML.OnnxTransformer", "Microsoft.ML.OnnxRuntime" ], + "usingStatements": [ "Microsoft.ML" ], + "searchOption": "dnn_featurizer_image_option" } ] } diff --git a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj index 2a223df06f..51b6d8eb36 100644 --- a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj +++ b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs index 6df898e0f3..8998151e5e 100644 --- a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs +++ b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - +using System; namespace Microsoft.ML.AutoML.CodeGen { internal partial class LoadImages @@ -44,4 +44,28 @@ public override IEstimator BuildFromOption(MLContext context, Imag return context.MulticlassClassification.Trainers.ImageClassification(param.LabelColumnName, param.FeatureColumnName, param.ScoreColumnName); } } + + internal partial class DnnFeaturizerImage + { + public override IEstimator BuildFromOption(MLContext context, DnnFeaturizerImageOption param) + { + switch (param.ModelFactory) + { + case "resnet_50": + return context.Transforms.DnnFeaturizeImage(param.OutputColumnName, + m => m.ModelSelector.ResNet50(context, param.OutputColumnName, param.InputColumnName), param.InputColumnName); + case "resnet_18": + return context.Transforms.DnnFeaturizeImage(param.OutputColumnName, + m => m.ModelSelector.ResNet18(context, param.OutputColumnName, param.InputColumnName), param.InputColumnName); + case "resnet_101": + return context.Transforms.DnnFeaturizeImage(param.OutputColumnName, + m => m.ModelSelector.ResNet101(context, param.OutputColumnName, param.InputColumnName), param.InputColumnName); + case "alexnet": + return context.Transforms.DnnFeaturizeImage(param.OutputColumnName, + m => m.ModelSelector.AlexNet(context, param.OutputColumnName, param.InputColumnName), param.InputColumnName); + default: + throw new NotImplementedException(); + } + } + } } diff --git a/tools-local/Microsoft.ML.AutoML.SourceGenerator/SearchSpaceGenerator.cs b/tools-local/Microsoft.ML.AutoML.SourceGenerator/SearchSpaceGenerator.cs index b36702c074..f92eb898e1 100644 --- a/tools-local/Microsoft.ML.AutoML.SourceGenerator/SearchSpaceGenerator.cs +++ b/tools-local/Microsoft.ML.AutoML.SourceGenerator/SearchSpaceGenerator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text.Json; @@ -52,6 +53,7 @@ public void Execute(GeneratorExecutionContext context) "anchor" => "Anchor", "colorBits" => "ColorBits", "colorsOrder" => "ColorsOrder", + "dnnModelFactory" => "string", _ => throw new ArgumentException("unknown type"), }; @@ -78,42 +80,38 @@ public void Execute(GeneratorExecutionContext context) // default option optionAttribution = string.Empty; } - else + else if (searchSpaceNode is JsonObject searchSpaceObject && searchSpaceObject.ContainsKey("min")) { - var searchSpaceObject = searchSpaceNode.AsObject(); - if (searchSpaceObject.ContainsKey("min")) + // range option + var minToken = searchSpaceNode["min"]; + var minValue = searchSpaceNode["min"].GetValue(); + var maxValue = searchSpaceNode["max"].GetValue(); + var logBase = searchSpaceObject.ContainsKey("log_base") is false ? "false" : searchSpaceNode["log_base"].GetValue() ? "true" : "false"; + optionAttribution = (optionTypeName, minValue, maxValue, logBase, optionDefaultValue) switch { - // range option - var minToken = searchSpaceNode["min"]; - var minValue = searchSpaceNode["min"].GetValue(); - var maxValue = searchSpaceNode["max"].GetValue(); - var logBase = searchSpaceObject.ContainsKey("log_base") is false ? "false" : searchSpaceNode["log_base"].GetValue() ? "true" : "false"; - optionAttribution = (optionTypeName, minValue, maxValue, logBase, optionDefaultValue) switch - { - ("int", _, _, _, null) => $"Range((int){Convert.ToInt32(minValue).ToString(CultureInfo.InvariantCulture)}, (int){Convert.ToInt32(maxValue).ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - ("float", _, _, _, null) => $"Range((float){Convert.ToSingle(minValue).ToString(CultureInfo.InvariantCulture)}, (float){Convert.ToSingle(maxValue).ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - ("double", _, _, _, null) => $"Range((double){minValue.ToString(CultureInfo.InvariantCulture)}, (double){maxValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - ("int", _, _, _, _) => $"Range((int){Convert.ToInt32(minValue).ToString(CultureInfo.InvariantCulture)}, (int){Convert.ToInt32(maxValue).ToString(CultureInfo.InvariantCulture)}, init: (int){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - ("float", _, _, _, _) => $"Range((float){Convert.ToSingle(minValue).ToString(CultureInfo.InvariantCulture)}, (float){Convert.ToSingle(maxValue).ToString(CultureInfo.InvariantCulture)}, init: (float){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - ("double", _, _, _, _) => $"Range((double){minValue.ToString(CultureInfo.InvariantCulture)}, (double){maxValue.ToString(CultureInfo.InvariantCulture)}, init: (double){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", - _ => throw new NotImplementedException(), - }; - optionAttribution = $"[{optionAttribution}]"; - } - else + ("int", _, _, _, null) => $"Range((int){Convert.ToInt32(minValue).ToString(CultureInfo.InvariantCulture)}, (int){Convert.ToInt32(maxValue).ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + ("float", _, _, _, null) => $"Range((float){Convert.ToSingle(minValue).ToString(CultureInfo.InvariantCulture)}, (float){Convert.ToSingle(maxValue).ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + ("double", _, _, _, null) => $"Range((double){minValue.ToString(CultureInfo.InvariantCulture)}, (double){maxValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + ("int", _, _, _, _) => $"Range((int){Convert.ToInt32(minValue).ToString(CultureInfo.InvariantCulture)}, (int){Convert.ToInt32(maxValue).ToString(CultureInfo.InvariantCulture)}, init: (int){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + ("float", _, _, _, _) => $"Range((float){Convert.ToSingle(minValue).ToString(CultureInfo.InvariantCulture)}, (float){Convert.ToSingle(maxValue).ToString(CultureInfo.InvariantCulture)}, init: (float){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + ("double", _, _, _, _) => $"Range((double){minValue.ToString(CultureInfo.InvariantCulture)}, (double){maxValue.ToString(CultureInfo.InvariantCulture)}, init: (double){optionDefaultValue.ToString(CultureInfo.InvariantCulture)}, logBase: {logBase.ToString(CultureInfo.InvariantCulture)})", + _ => throw new NotImplementedException(), + }; + optionAttribution = $"[{optionAttribution}]"; + } + else + { + // choice option + var values = searchSpaceNode.AsArray().Select(x => x.Deserialize()); + var valuesParam = optionTypeName switch { - // choice option - var values = searchSpaceNode["value"].GetValue(); - var valuesParam = optionTypeName switch - { - "int" => $"new object[]{{ {string.Join(",", values)} }}", - "boolean" => $"new object[]{{ {string.Join(",", values)} }}", - "string" => $"new object[]{{ {string.Join(",", values.Select(x => $"\"{x}\""))} }}", - _ => throw new NotImplementedException("only support int|boolean|string"), - }; + "int" => $"new object[]{{ {string.Join(",", values)} }}", + "boolean" => $"new object[]{{ {string.Join(",", values)} }}", + "string" => $"new object[]{{ {string.Join(",", values.Select(x => $"\"{x}\""))} }}", + _ => throw new NotImplementedException("only support int|boolean|string"), + }; - optionAttribution = optionDefaultValue == null ? $"[Choice({valuesParam})]" : $"[Choice({valuesParam}, {optionDefaultValue})]"; - } + optionAttribution = optionDefaultValue == null ? $"[Choice({valuesParam})]" : $"[Choice({valuesParam}, {optionDefaultValue})]"; } return (optionTypeName, optionName, optionAttribution, optionDefaultValue); From 42379ce3b4230c7656ca439b1d3b891ae0547c2d Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Tue, 26 Jul 2022 11:04:02 -0700 Subject: [PATCH 2/3] add tests --- .../Microsoft.ML.AutoML.csproj | 4 ++ ...sts.AutoFeaturizer_image_test.approved.txt | 50 +++++++++++++++++++ ...Tests.ImagePathFeaturizerTest.approved.txt | 41 +++++++++++++++ .../AutoFeaturizerTests.cs | 27 ++++++++++ 4 files changed, 122 insertions(+) create mode 100644 test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.AutoFeaturizer_image_test.approved.txt create mode 100644 test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.ImagePathFeaturizerTest.approved.txt diff --git a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj index 51b6d8eb36..40d5c76ae5 100644 --- a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj +++ b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj @@ -35,6 +35,10 @@ all + + + + all diff --git a/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.AutoFeaturizer_image_test.approved.txt b/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.AutoFeaturizer_image_test.approved.txt new file mode 100644 index 0000000000..4f137b8a8a --- /dev/null +++ b/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.AutoFeaturizer_image_test.approved.txt @@ -0,0 +1,50 @@ +{ + "schema": "e0 * e1 * e2 * e3 * e4", + "estimators": { + "e0": { + "estimatorType": "LoadImages", + "parameter": { + "OutputColumnName": "ImagePath", + "InputColumnName": "ImagePath" + } + }, + "e1": { + "estimatorType": "ResizeImages", + "parameter": { + "OutputColumnName": "ImagePath", + "InputColumnName": "ImagePath", + "ImageHeight": 224, + "ImageWidth": 224, + "CropAnchor": "Center", + "Resizing": "Fill" + } + }, + "e2": { + "estimatorType": "ExtractPixels", + "parameter": { + "OutputColumnName": "ImagePath", + "InputColumnName": "ImagePath", + "ColorsToExtract": "Rgb", + "OrderOfExtraction": "ARGB", + "OutputAsFloatArray": true + } + }, + "e3": { + "estimatorType": "DnnFeaturizerImage", + "parameter": { + "OutputColumnName": "ImagePath", + "InputColumnName": "ImagePath", + "ModelFactory": "resnet_18" + } + }, + "e4": { + "estimatorType": "Concatenate", + "parameter": { + "InputColumnNames": [ + "ImagePath" + ], + "OutputColumnName": "Features" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.ImagePathFeaturizerTest.approved.txt b/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.ImagePathFeaturizerTest.approved.txt new file mode 100644 index 0000000000..0b930b01aa --- /dev/null +++ b/test/Microsoft.ML.AutoML.Tests/ApprovalTests/AutoFeaturizerTests.ImagePathFeaturizerTest.approved.txt @@ -0,0 +1,41 @@ +{ + "schema": "e0 * e1 * e2 * e3", + "estimators": { + "e0": { + "estimatorType": "LoadImages", + "parameter": { + "OutputColumnName": "imagePath", + "InputColumnName": "imagePath" + } + }, + "e1": { + "estimatorType": "ResizeImages", + "parameter": { + "OutputColumnName": "imagePath", + "InputColumnName": "imagePath", + "ImageHeight": 224, + "ImageWidth": 224, + "CropAnchor": "Center", + "Resizing": "Fill" + } + }, + "e2": { + "estimatorType": "ExtractPixels", + "parameter": { + "OutputColumnName": "imagePath", + "InputColumnName": "imagePath", + "ColorsToExtract": "Rgb", + "OrderOfExtraction": "ARGB", + "OutputAsFloatArray": true + } + }, + "e3": { + "estimatorType": "DnnFeaturizerImage", + "parameter": { + "OutputColumnName": "imagePath", + "InputColumnName": "imagePath", + "ModelFactory": "resnet_18" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.AutoML.Tests/AutoFeaturizerTests.cs b/test/Microsoft.ML.AutoML.Tests/AutoFeaturizerTests.cs index aa66207921..47abced6dc 100644 --- a/test/Microsoft.ML.AutoML.Tests/AutoFeaturizerTests.cs +++ b/test/Microsoft.ML.AutoML.Tests/AutoFeaturizerTests.cs @@ -61,5 +61,32 @@ public void AutoFeaturizer_iris_test() Approvals.Verify(JsonSerializer.Serialize(pipeline, _jsonSerializerOptions)); } + + [Fact] + [UseReporter(typeof(DiffReporter))] + [UseApprovalSubdirectory("ApprovalTests")] + public void ImagePathFeaturizerTest() + { + var context = new MLContext(1); + var pipeline = context.Auto().ImagePathFeaturizer("imagePath", "imagePath"); + + Approvals.Verify(JsonSerializer.Serialize(pipeline, _jsonSerializerOptions)); + } + + + [Fact] + [UseReporter(typeof(DiffReporter))] + [UseApprovalSubdirectory("ApprovalTests")] + public void AutoFeaturizer_image_test() + { + var context = new MLContext(1); + var datasetPath = DatasetUtil.GetFlowersDataset(); + var columnInference = context.Auto().InferColumns(datasetPath, "Label"); + var textLoader = context.Data.CreateTextLoader(columnInference.TextLoaderOptions); + var trainData = textLoader.Load(datasetPath); + var pipeline = context.Auto().Featurizer(trainData, columnInference.ColumnInformation); + + Approvals.Verify(JsonSerializer.Serialize(pipeline, _jsonSerializerOptions)); + } } } From db2d19889130cb981010ebebf1c47c680d4d9c4c Mon Sep 17 00:00:00 2001 From: Xiaoyun Zhang Date: Tue, 26 Jul 2022 12:23:20 -0700 Subject: [PATCH 3/3] Update Microsoft.ML.AutoML.csproj --- src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj index 40d5c76ae5..d25012a3d4 100644 --- a/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj +++ b/src/Microsoft.ML.AutoML/Microsoft.ML.AutoML.csproj @@ -65,7 +65,6 @@ -