| File: Dynamic\Transforms\CustomMappingSaveAndLoad.cs | Web Access |
| Project: src\docs\samples\Microsoft.ML.Samples\Microsoft.ML.Samples.csproj (Microsoft.ML.Samples) |
using System; using System.Collections.Generic; using Microsoft.ML; using Microsoft.ML.Transforms; namespace Samples.Dynamic { public static class CustomMappingSaveAndLoad { // This example shows how to define and apply a custom mapping of input // columns to output columns with a contract name. The contract name is // used in the CustomMappingFactoryAttribute that decorates the custom // mapping action. The pipeline containing the custom mapping can then be // saved to disk, and it can be loaded back after the assembly containing // the custom mapping action is registered. public static void Example() { // Create a new ML context, for ML.NET operations. It can be used for // exception tracking and logging, as well as the source of randomness. var mlContext = new MLContext(); // Get a small dataset as an IEnumerable and convert it to an IDataView. var samples = new List<InputData> { new InputData { Age = 26 }, new InputData { Age = 35 }, new InputData { Age = 34 }, new InputData { Age = 28 }, }; var data = mlContext.Data.LoadFromEnumerable(samples); // Custom transformations can be used to transform data directly, or as // part of a pipeline of estimators. The contractName must be provided // in order for a pipeline containing a CustomMapping estimator to be // saved and loaded back. The contractName must be the same as in the // CustomMappingFactoryAttribute used to decorate the custom action // defined by the user. var pipeline = mlContext.Transforms.CustomMapping(new IsUnderThirtyCustomAction().GetMapping(), contractName: "IsUnderThirty"); var transformer = pipeline.Fit(data); // To save and load the CustomMapping estimator, the assembly in which // the custom action is defined needs to be registered in the // environment. The following registers the assembly where // IsUnderThirtyCustomAction is defined. // This is necessary only in versions v1.5-preview2 and earlier mlContext.ComponentCatalog.RegisterAssembly(typeof( IsUnderThirtyCustomAction).Assembly); // Now the transform pipeline can be saved and loaded through the usual // MLContext method. mlContext.Model.Save(transformer, data.Schema, "customTransform.zip"); var loadedTransform = mlContext.Model.Load("customTransform.zip", out var inputSchema); // Now we can transform the data and look at the output to confirm the // behavior of the estimator. This operation doesn't actually evaluate // data until we read the data below. var transformedData = loadedTransform.Transform(data); var dataEnumerable = mlContext.Data.CreateEnumerable<TransformedData>( transformedData, reuseRowObject: true); Console.WriteLine("Age\tIsUnderThirty"); foreach (var row in dataEnumerable) Console.WriteLine($"{row.Age}\t {row.IsUnderThirty}"); // Expected output: // Age IsUnderThirty // 26 True // 35 False // 34 False // 28 True } // The custom action needs to implement the abstract class // CustomMappingFactory, and needs to have attribute // CustomMappingFactoryAttribute with argument equal to the contractName // used to define the CustomMapping estimator which uses the action. [CustomMappingFactoryAttribute("IsUnderThirty")] private class IsUnderThirtyCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> { // We define the custom mapping between input and output rows that will // be applied by the transformation. public static void CustomAction(InputData input, CustomMappingOutput output) => output.IsUnderThirty = input.Age < 30; public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction; } // Defines only the column to be generated by the custom mapping // transformation in addition to the columns already present. private class CustomMappingOutput { public bool IsUnderThirty { get; set; } } // Defines the schema of the input data. private class InputData { public float Age { get; set; } } // Defines the schema of the transformed data, which includes the new column // IsUnderThirty. private class TransformedData : InputData { public bool IsUnderThirty { get; set; } } } }