Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FeatureColumnName in the public API #2990

Merged
merged 1 commit into from Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -35,12 +35,12 @@ public static void Example()
// Create a Feature Contribution Calculator
// Calculate the feature contributions for all features given trained model parameters
// And don't normalize the contribution scores
var featureContributionCalculator = mlContext.Model.Explainability.FeatureContributionCalculation(model.Model, model.FeatureColumn, numPositiveContributions: 11, normalize: false);
var featureContributionCalculator = mlContext.Model.Explainability.FeatureContributionCalculation(model.Model, model.FeatureColumnName, numPositiveContributions: 11, normalize: false);
var outputData = featureContributionCalculator.Fit(scoredData).Transform(scoredData);

// FeatureContributionCalculatingEstimator can be use as an intermediary step in a pipeline.
// The features retained by FeatureContributionCalculatingEstimator will be in the FeatureContribution column.
var pipeline = mlContext.Model.Explainability.FeatureContributionCalculation(model.Model, model.FeatureColumn, numPositiveContributions: 11)
var pipeline = mlContext.Model.Explainability.FeatureContributionCalculation(model.Model, model.FeatureColumnName, numPositiveContributions: 11)
.Append(mlContext.Regression.Trainers.Ols(featureColumnName: "FeatureContributions"));
var outData = featureContributionCalculator.Fit(scoredData).Transform(scoredData);

Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.ML.Data/Prediction/CalibratorCatalog.cs
Expand Up @@ -167,7 +167,7 @@ private protected CalibratorTransformer(IHostEnvironment env, ModelLoadContext c
ctx.LoadModel<TICalibrator, SignatureLoadModel>(env, out _calibrator, "Calibrator");
}

string ISingleFeaturePredictionTransformer<TICalibrator>.FeatureColumn => DefaultColumnNames.Score;
string ISingleFeaturePredictionTransformer<TICalibrator>.FeatureColumnName => DefaultColumnNames.Score;

DataViewType ISingleFeaturePredictionTransformer<TICalibrator>.FeatureColumnType => NumberDataViewType.Single;

Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.ML.Data/Prediction/IPredictionTransformer.cs
Expand Up @@ -19,15 +19,15 @@ public interface IPredictionTransformer<out TModel> : ITransformer
}

/// <summary>
/// An ISingleFeaturePredictionTransformer contains the name of the <see cref="FeatureColumn"/>
/// An ISingleFeaturePredictionTransformer contains the name of the <see cref="FeatureColumnName"/>
/// and its type, <see cref="FeatureColumnType"/>. Implementations of this interface, have the ability
/// to score the data of an input <see cref="IDataView"/> through the <see cref="ITransformer.Transform(IDataView)"/>
/// </summary>
/// <typeparam name="TModel">The <see cref="IPredictor"/> or <see cref="ICalibrator"/> used for the data transformation.</typeparam>
public interface ISingleFeaturePredictionTransformer<out TModel> : IPredictionTransformer<TModel>
{
/// <summary>The name of the feature column.</summary>
string FeatureColumn { get; }
string FeatureColumnName { get; }

/// <summary>Holds information about the type of the feature column.</summary>
DataViewType FeatureColumnType { get; }
Expand Down
32 changes: 16 additions & 16 deletions src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs
Expand Up @@ -173,7 +173,7 @@ public abstract class SingleFeaturePredictionTransformerBase<TModel> : Predictio
/// <summary>
/// The name of the feature column used by the prediction transformer.
/// </summary>
public string FeatureColumn { get; }
public string FeatureColumnName { get; }

/// <summary>
/// The type of the prediction transformer
Expand All @@ -190,7 +190,7 @@ public abstract class SingleFeaturePredictionTransformerBase<TModel> : Predictio
private protected SingleFeaturePredictionTransformerBase(IHost host, TModel model, DataViewSchema trainSchema, string featureColumn)
: base(host, model, trainSchema)
{
FeatureColumn = featureColumn;
FeatureColumnName = featureColumn;
if (featureColumn == null)
FeatureColumnType = null;
else if (!trainSchema.TryGetColumnIndex(featureColumn, out int col))
Expand All @@ -204,12 +204,12 @@ private protected SingleFeaturePredictionTransformerBase(IHost host, TModel mode
private protected SingleFeaturePredictionTransformerBase(IHost host, ModelLoadContext ctx)
: base(host, ctx)
{
FeatureColumn = ctx.LoadStringOrNull();
FeatureColumnName = ctx.LoadStringOrNull();

if (FeatureColumn == null)
if (FeatureColumnName == null)
FeatureColumnType = null;
else if (!TrainSchema.TryGetColumnIndex(FeatureColumn, out int col))
throw Host.ExceptSchemaMismatch(nameof(FeatureColumn), "feature", FeatureColumn);
else if (!TrainSchema.TryGetColumnIndex(FeatureColumnName, out int col))
throw Host.ExceptSchemaMismatch(nameof(FeatureColumnName), "feature", FeatureColumnName);
else
FeatureColumnType = TrainSchema[col].Type;

Expand All @@ -225,12 +225,12 @@ public sealed override DataViewSchema GetOutputSchema(DataViewSchema inputSchema
{
Host.CheckValue(inputSchema, nameof(inputSchema));

if (FeatureColumn != null)
if (FeatureColumnName != null)
{
if (!inputSchema.TryGetColumnIndex(FeatureColumn, out int col))
throw Host.ExceptSchemaMismatch(nameof(inputSchema), "feature", FeatureColumn);
if (!inputSchema.TryGetColumnIndex(FeatureColumnName, out int col))
throw Host.ExceptSchemaMismatch(nameof(inputSchema), "feature", FeatureColumnName);
if (!inputSchema[col].Type.Equals(FeatureColumnType))
throw Host.ExceptSchemaMismatch(nameof(inputSchema), "feature", FeatureColumn, FeatureColumnType.ToString(), inputSchema[col].Type.ToString());
throw Host.ExceptSchemaMismatch(nameof(inputSchema), "feature", FeatureColumnName, FeatureColumnType.ToString(), inputSchema[col].Type.ToString());
}

return Transform(new EmptyDataView(Host, inputSchema)).Schema;
Expand All @@ -246,12 +246,12 @@ private protected sealed override void SaveModel(ModelSaveContext ctx)
private protected virtual void SaveCore(ModelSaveContext ctx)
{
SaveModelCore(ctx);
ctx.SaveStringOrNull(FeatureColumn);
ctx.SaveStringOrNull(FeatureColumnName);
}

private protected GenericScorer GetGenericScorer()
{
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn);
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumnName);
return new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
}
}
Expand Down Expand Up @@ -293,7 +293,7 @@ internal AnomalyPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx

private void SetScorer()
{
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn);
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumnName);
var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn };
Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
}
Expand Down Expand Up @@ -362,7 +362,7 @@ internal BinaryPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx)

private void SetScorer()
{
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn);
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumnName);
var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn };
Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
}
Expand Down Expand Up @@ -426,7 +426,7 @@ internal MulticlassPredictionTransformer(IHostEnvironment env, ModelLoadContext

private void SetScorer()
{
var schema = new RoleMappedSchema(TrainSchema, _trainLabelColumn, FeatureColumn);
var schema = new RoleMappedSchema(TrainSchema, _trainLabelColumn, FeatureColumnName);
var args = new MulticlassClassificationScorer.Arguments();
Scorer = new MulticlassClassificationScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
}
Expand Down Expand Up @@ -565,7 +565,7 @@ internal ClusteringPredictionTransformer(IHostEnvironment env, ModelLoadContext
// *** Binary format ***
// <base info>

var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn);
var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumnName);
var args = new ClusteringScorer.Arguments();
Scorer = new ClusteringScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.ML.Data/Transforms/ExplainabilityCatalog.cs
Expand Up @@ -18,18 +18,18 @@ public static class ExplainabilityCatalog
/// </summary>
/// <param name="catalog">The model explainability operations catalog.</param>
/// <param name="modelParameters">Trained model parameters that support Feature Contribution Calculation and which will be used for scoring.</param>
/// <param name="featureColumn">The name of the feature column that will be used as input.</param>
/// <param name="featureColumnName">The name of the feature column that will be used as input.</param>
/// <param name="numPositiveContributions">The number of positive contributions to report, sorted from highest magnitude to lowest magnitude.
/// Note that if there are fewer features with positive contributions than <paramref name="numPositiveContributions"/>, the rest will be returned as zeros.</param>
/// <param name="numNegativeContributions">The number of negative contributions to report, sorted from highest magnitude to lowest magnitude.
/// Note that if there are fewer features with negative contributions than <paramref name="numNegativeContributions"/>, the rest will be returned as zeros.</param>
/// <param name="normalize">Whether the feature contributions should be normalized to the [-1, 1] interval.</param>
public static FeatureContributionCalculatingEstimator FeatureContributionCalculation(this ModelOperationsCatalog.ExplainabilityTransforms catalog,
ICalculateFeatureContribution modelParameters,
string featureColumn = DefaultColumnNames.Features,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

featureColumn = [](start = 19, length = 16)

#2966 rewrites these functions, so you can drop changes to ExplainabilityCatalog.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be much appreciated indeed :)

string featureColumnName = DefaultColumnNames.Features,
int numPositiveContributions = FeatureContributionDefaults.NumPositiveContributions,
int numNegativeContributions = FeatureContributionDefaults.NumNegativeContributions,
bool normalize = FeatureContributionDefaults.Normalize)
=> new FeatureContributionCalculatingEstimator(CatalogUtils.GetEnvironment(catalog), modelParameters, featureColumn, numPositiveContributions, numNegativeContributions, normalize);
=> new FeatureContributionCalculatingEstimator(CatalogUtils.GetEnvironment(catalog), modelParameters, featureColumnName, numPositiveContributions, numNegativeContributions, normalize);
}
}
Expand Up @@ -131,16 +131,16 @@ private ISingleFeaturePredictionTransformer<TScalarPredictor> TrainOne(IChannel

// REVIEW: restoring the RoleMappedData, as much as we can.
// not having the weight column on the data passed to the TrainCalibrator should be addressed.
var trainedData = new RoleMappedData(view, label: trainerLabel, feature: transformer.FeatureColumn);
var trainedData = new RoleMappedData(view, label: trainerLabel, feature: transformer.FeatureColumnName);

if (calibratedModel == null)
calibratedModel = CalibratorUtils.GetCalibratedPredictor(Host, ch, Calibrator, transformer.Model, trainedData, Args.MaxCalibrationExamples) as TDistPredictor;

Host.Check(calibratedModel != null, "Calibrated predictor does not implement the expected interface");
return new BinaryPredictionTransformer<TScalarPredictor>(Host, calibratedModel, trainedData.Data.Schema, transformer.FeatureColumn);
return new BinaryPredictionTransformer<TScalarPredictor>(Host, calibratedModel, trainedData.Data.Schema, transformer.FeatureColumnName);
}

return new BinaryPredictionTransformer<TScalarPredictor>(Host, transformer.Model, view.Schema, transformer.FeatureColumn);
return new BinaryPredictionTransformer<TScalarPredictor>(Host, transformer.Model, view.Schema, transformer.FeatureColumnName);
}

private IDataView MapLabels(RoleMappedData data, int cls)
Expand Down Expand Up @@ -182,7 +182,7 @@ public override MulticlassPredictionTransformer<OneVersusAllModelParameters> Fit
if (i == 0)
{
var transformer = TrainOne(ch, Trainer, td, i);
featureColumn = transformer.FeatureColumn;
featureColumn = transformer.FeatureColumnName;
}

predictors[i] = TrainOne(ch, Trainer, td, i).Model;
Expand Down
Expand Up @@ -131,13 +131,13 @@ private ISingleFeaturePredictionTransformer<TDistPredictor> TrainOne(IChannel ch
var transformer = trainer.Fit(view);

// the validations in the calibrator check for the feature column, in the RoleMappedData
var trainedData = new RoleMappedData(view, label: trainerLabel, feature: transformer.FeatureColumn);
var trainedData = new RoleMappedData(view, label: trainerLabel, feature: transformer.FeatureColumnName);

var calibratedModel = transformer.Model as TDistPredictor;
if (calibratedModel == null)
calibratedModel = CalibratorUtils.GetCalibratedPredictor(Host, ch, Calibrator, transformer.Model, trainedData, Args.MaxCalibrationExamples) as TDistPredictor;

return new BinaryPredictionTransformer<TDistPredictor>(Host, calibratedModel, trainedData.Data.Schema, transformer.FeatureColumn);
return new BinaryPredictionTransformer<TDistPredictor>(Host, calibratedModel, trainedData.Data.Schema, transformer.FeatureColumnName);
}

private IDataView MapLabels(RoleMappedData data, int cls1, int cls2)
Expand Down Expand Up @@ -188,7 +188,7 @@ public override TTransformer Fit(IDataView input)
if (i == 0 && j == 0)
{
var transformer = TrainOne(ch, Trainer, td, i, j);
featureColumn = transformer.FeatureColumn;
featureColumn = transformer.FeatureColumnName;
}

predictors[i][j] = TrainOne(ch, Trainer, td, i, j).Model;
Expand Down
8 changes: 4 additions & 4 deletions test/Microsoft.ML.Functional.Tests/Explainability.cs
Expand Up @@ -152,7 +152,7 @@ public void LocalFeatureImportanceForLinearModel()

// Create a Feature Contribution Calculator.
var predictor = model.LastTransformer;
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumn, normalize: false);
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumnName, normalize: false);

// Compute the contributions
var outputData = featureContributions.Fit(scoredData).Transform(scoredData);
Expand Down Expand Up @@ -189,7 +189,7 @@ public void LocalFeatureImportanceForFastTreeModel()

// Create a Feature Contribution Calculator.
var predictor = model.LastTransformer;
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumn, normalize: false);
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumnName, normalize: false);

// Compute the contributions
var outputData = featureContributions.Fit(scoredData).Transform(scoredData);
Expand Down Expand Up @@ -226,7 +226,7 @@ public void LocalFeatureImportanceForFastForestModel()

// Create a Feature Contribution Calculator.
var predictor = model.LastTransformer;
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumn, normalize: false);
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumnName, normalize: false);

// Compute the contributions
var outputData = featureContributions.Fit(scoredData).Transform(scoredData);
Expand Down Expand Up @@ -264,7 +264,7 @@ public void LocalFeatureImportanceForGamModel()

// Create a Feature Contribution Calculator.
var predictor = model.LastTransformer;
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumn, normalize: false);
var featureContributions = mlContext.Model.Explainability.FeatureContributionCalculation(predictor.Model, predictor.FeatureColumnName, normalize: false);

// Compute the contributions
var outputData = featureContributions.Fit(scoredData).Transform(scoredData);
Expand Down
10 changes: 5 additions & 5 deletions test/Microsoft.ML.Tests/FeatureContributionTests.cs
Expand Up @@ -30,11 +30,11 @@ public void FeatureContributionEstimatorWorkout()
var data = GetSparseDataset();
var model = ML.Regression.Trainers.Ols().Fit(data);

var estPipe = new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumn)
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumn, normalize: false))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumn, numPositiveContributions: 0))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumn, numNegativeContributions: 0))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumn, numPositiveContributions: 0, numNegativeContributions: 0));
var estPipe = new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumnName)
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumnName, normalize: false))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumnName, numPositiveContributions: 0))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumnName, numNegativeContributions: 0))
.Append(new FeatureContributionCalculatingEstimator(ML, model.Model, model.FeatureColumnName, numPositiveContributions: 0, numNegativeContributions: 0));

TestEstimatorCore(estPipe, data);
Done();
Expand Down