ML.NET中为IDataView添加多个自定义对数列及转换失效问题求解
问题根因
你使用了同一个CustomMappingOutput类作为两个独立自定义转换的输出类型。第二个LogThrust转换执行时,其输出对象会被初始化,CustomAction仅对LogThrust赋值,未被赋值的LogVelocity会被重置为double类型的默认值0,直接覆盖了第一步转换的计算结果。
解决方案
方案1:合并自定义映射(推荐)
将两个列的对数计算合并到同一个自定义映射中,避免多步转换的字段覆盖问题,同时减少不必要的计算开销:
using Microsoft.ML; using Microsoft.ML.Transforms; using System; using System.Collections.Generic; using System.IO; namespace TestLog { public static class Program { private class InputData { public double Velocity { get; set; } public double Thrust { get; set; } } private class CustomMappingOutput { public double LogVelocity { get; set; } public double LogThrust { get; set; } } private class TransformedData : InputData { public double LogVelocity { get; set; } public double LogThrust { get; set; } } [CustomMappingFactoryAttribute("LogTransform")] private class LogCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> { public static void CustomAction(InputData input, CustomMappingOutput output) { output.LogVelocity = Math.Log(input.Velocity); output.LogThrust = Math.Log(input.Thrust); } public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction; } public static void Run() { var mlContext = new MLContext(); var samples = new List<InputData> { new InputData { Velocity= 0.006467, Thrust = 1.614237 }, new InputData { Velocity= 0.53451, Thrust = 1.068356 }, new InputData { Velocity= 0.278578, Thrust = 0.216861 }, new InputData { Velocity= 0.014179, Thrust = 0.119712 }, new InputData { Velocity= 0.392814, Thrust = 3.915486 } }; var data = mlContext.Data.LoadFromEnumerable(samples); var pipeline = mlContext.Transforms.CustomMapping(new LogCustomAction().GetMapping(), contractName: "LogTransform"); var transformer = pipeline.Fit(data); // 保存转换管道方便其他进程加载使用 mlContext.Model.Save(transformer, data.Schema, "customTransform.zip"); // 模拟其他程序加载已保存的转换模型 var loadedTransform = mlContext.Model.Load("customTransform.zip", out _); // 对数据执行转换 var transformedIDataView = loadedTransform.Transform(data); var newDataEnumerable = mlContext.Data.CreateEnumerable<TransformedData>(transformedIDataView, reuseRowObject: true); var newIDataView = mlContext.Data.LoadFromEnumerable(newDataEnumerable); // 保存转换后的IDataView为CSV文件方便校验转换结果 var path = @"../../../transformed.csv"; using var stream = File.Create(path); mlContext.Data.SaveAsText(newIDataView, stream, separatorChar: ',', headerRow: true, schema: false); } static void Main() { Program.Run(); Console.WriteLine("按下回车键退出"); Console.ReadLine(); } } }
方案2:使用ML.NET内置对数变换(最优)
你的场景完全可以用ML.NET自带的逐元素对数变换实现,无需自定义映射,性能更高、保存加载没有自定义契约的兼容问题,仅需替换管道定义部分即可:
var pipeline = mlContext.Transforms.Elementwise.Log(outputColumnName: "LogVelocity", inputColumnName: nameof(InputData.Velocity)) .Append(mlContext.Transforms.Elementwise.Log(outputColumnName: "LogThrust", inputColumnName: nameof(InputData.Thrust)));
内容的提问来源于stack exchange,提问作者user274610
相关产品推荐
相关产品推荐

