System information
- OS version/distro: Windows 10 Pro x64
- .NET Version (eg., dotnet --info): .NET Core 3.0
- ML.NET Version: 1.5.0-preview
Issue
Trying to use variable number of properties (dynamic schema) for the trainer using dataView.SelectColumns. This creates correct trainer with only 2 features, but prediction engine still requires to specify original input model and uses all 10+ features, even though all features except selected 2 were set to 0.
What did you do?
- use input model with 10 features / properties
- create data view and select only 2 of these features
- use LGBM as a trainer
- create 3 input items with labels - Strategy1, Strategy2, Strategy3 and train estimator
- try to make prediction providing test item identical to Strategy3
What happened?
- output schema in CreatePredictionEngine shows that there are 10+ columns, even though, when I created a data view for training, I selected only 2 features
- result of prediction is always the same - Strategy1, most probably because trainer always compares 10+ features instead of 2, even though all features except selected 2 were set to 0
What did you expect?
- if estimator was trained to use only 2 features / input properties, then prediction engine should use provided data view schema and should also work only with 2 selected properties
- in the code below I'd like to make sure that properties Contrast, Param1 ... Param5 are ignored by prediction engine
Source code / logs
publicclassMyInputModel{[ColumnName(nameof(PredictorLabelsEnum.Strategy)),LoadColumn(0)]publicstringStrategy{get;set;}[ColumnName(nameof(InputNamesEnum.Pitch)),LoadColumn(1)]publicfloatPitch{get;set;}[ColumnName(nameof(InputNamesEnum.Energy)),LoadColumn(2)]publicfloatEnergy{get;set;}[ColumnName(nameof(InputNamesEnum.Contrast)),LoadColumn(3,8),VectorType(6)]publicfloat[]Contrast{get;set;}[ColumnName(nameof(InputNamesEnum.Param1)),LoadColumn(9)]publicfloatParam1{get;set;}[ColumnName(nameof(InputNamesEnum.Param2)),LoadColumn(10)]publicfloatParam2{get;set;}[ColumnName(nameof(InputNamesEnum.Param3)),LoadColumn(11)]publicfloatParam3{get;set;}[ColumnName(nameof(InputNamesEnum.Param4)),LoadColumn(12)]publicfloatParam4{get;set;}[ColumnName(nameof(InputNamesEnum.Param5)),LoadColumn(13)]publicfloatParam5{get;set;}}publicIEstimator<ITransformer>GetPipeline(IEnumerable<string>columns){varpipeline=Context.Transforms.Conversion.MapValueToKey(new[]{newInputOutputColumnPair("Label","Strategy")})// use property "strategy" as categorizable label.Append(Context.Transforms.Concatenate("Combination",columns.ToArray()))// merge properties selected for analysis into "Combination".Append(Context.Transforms.NormalizeMinMax(new[]{newInputOutputColumnPair("Features","Combination")}));// normalize selected properties as "Features"returnpipeline;}publicIEstimator<ITransformer>GetEstimator(){varestimator=Context.MulticlassClassification.Trainers.LightGbm().Append(Context.Transforms.Conversion.MapKeyToValue(new[]{newInputOutputColumnPair("Prediction","PredictedLabel")}));returnestimator;}publicbyte[]SaveModel(IEnumerable<MyInputModel>items){varcolumns=new[]{"Pitch","Energy"};varestimator=GetEstimator();varpipeline=GetPipeline(columns);varsourceInputs=Context.Data.LoadFromEnumerable(items);varinputs=Context.Transforms.SelectColumns(columns.Concat(newList<string>{"Strategy"}).ToArray())// model has ~10 properties, we select only 2 of them .Fit(sourceInputs).Transform(sourceInputs);varpipelineModel=pipeline.Fit(inputs);varpipelineView=pipelineModel.Transform(inputs);varestimatorModel=pipeline.Append(estimator).Fit(inputs);varmodel=newbyte[0];using(varmemoryStream=newMemoryStream()){Context.Model.Save(estimatorModel,pipelineView.Schema,memoryStream);model=memoryStream.ToArray();}returnmodel;}publicstringLoadModelAndEstimate(byte[]predictor){varprediction=string.Empty;// let's make input identical to Strategy3, but somehow predicted result is still Strategy1varinput=newMyInputModel{Pitch=50,Energy=10,Contrast=new[]{0,0,0,0,0,0},Param1=0,Param2=0,Param3=0,Param4=0,Param5=0};using(varstream=newMemoryStream(predictor)){varmodel=Context.Model.Load(stream,outvarschema)asTransformerChain<ITransformer>;varchain=(model.LastTransformerasIEnumerable<ITransformer>).First()asMulticlassPredictionTransformer<OneVersusAllModelParameters>;varchainModel=chain.ModelasOneVersusAllModelParameters;// here I see only 3 properties with weights - Pitch, Energy, Labelvarengine=Context.Model.CreatePredictionEngine<MyInputModel,MyOutputModel>(model);// here output schema shows 10+ columns, even though I expect 3// also tried to specify data view schema from the model explicitly for prediction engine// var engine = Context.Model.CreatePredictionEngine<MyInputModel, MyOutputModel>(model, schema); prediction=engine.Predict(input);}returnprediction;}Example
var testData = [
{ Strategy = "Strategy1",
Pitch = 115,
Energy = 50,
Contrast = new [] { 0, 0, 0, 0, 0, 0 },
Param1 = 0, Param2 = 0, Param3 = 0, Param4 = 0, Param5 = 0
},
{
Strategy = "Strategy2",
Pitch = 90,
Energy = 30,
Contrast = new [] { 0, 0, 0, 0, 0, 0 },
Param1 = 0, Param2 = 0, Param3 = 0, Param4 = 0, Param5 = 0
},
{
Strategy = "Strategy3",
Pitch = 50,
Energy = 10,
Contrast = new [] { 0, 0, 0, 0, 0, 0 },
Param1 = 0, Param2 = 0, Param3 = 0, Param4 = 0, Param5 = 0
}
]
var trainData = [
{
Strategy = "Strategy3",
Pitch = 50,
Energy = 10,
Contrast = new [] { 0, 0, 0, 0, 0, 0 },
Param1 = 0, Param2 = 0, Param3 = 0, Param4 = 0, Param5 = 0
}
]
System information
Issue
Trying to use variable number of properties (dynamic schema) for the trainer using dataView.SelectColumns. This creates correct trainer with only 2 features, but prediction engine still requires to specify original input model and uses all 10+ features, even though all features except selected 2 were set to 0.
What did you do?
What happened?
What did you expect?
Source code / logs
Example