From 662d19c731790fc106596c8896f64f79e9020f41 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Fri, 21 Sep 2018 16:12:31 -0700 Subject: [PATCH 1/8] adding test +fit and finish --- .../Scorers/BinaryClassifierScorer.cs | 4 +- .../Scorers/PredictionTransformer.cs | 6 +- src/Microsoft.ML.PCA/PcaTrainer.cs | 86 ++++++++++++++++--- .../TrainerEstimators/TrainerEstimators.cs | 27 ++++++ 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs b/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs index ee76b5a674..3d7d399525 100644 --- a/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs +++ b/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs @@ -297,8 +297,6 @@ private static ColumnType GetPredColType(ColumnType scoreType, ISchemaBoundRowMa } private static bool OutputTypeMatches(ColumnType scoreType) - { - return scoreType == NumberType.Float; - } + => scoreType == NumberType.Float; } } diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index aa71ac2dc5..d7042d04dd 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -128,7 +128,7 @@ public abstract class SingleFeaturePredictionTransformerBase : Predictio public ColumnType FeatureColumnType { get; } public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema trainSchema, string featureColumn) - :base(host, model, trainSchema) + : base(host, model, trainSchema) { FeatureColumn = featureColumn; @@ -144,7 +144,7 @@ public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema } internal SingleFeaturePredictionTransformerBase(IHost host, ModelLoadContext ctx) - :base(host, ctx) + : base(host, ctx) { FeatureColumn = ctx.LoadStringOrNull(); @@ -162,7 +162,7 @@ public override ISchema GetOutputSchema(ISchema inputSchema) { Host.CheckValue(inputSchema, nameof(inputSchema)); - if(FeatureColumn != null) + if (FeatureColumn != null) { if (!inputSchema.TryGetColumnIndex(FeatureColumn, out int col)) throw Host.ExceptSchemaMismatch(nameof(inputSchema), RoleMappedSchema.ColumnRole.Feature.Value, FeatureColumn, FeatureColumnType.ToString(), null); diff --git a/src/Microsoft.ML.PCA/PcaTrainer.cs b/src/Microsoft.ML.PCA/PcaTrainer.cs index 2654af5d13..eb9d242db8 100644 --- a/src/Microsoft.ML.PCA/PcaTrainer.cs +++ b/src/Microsoft.ML.PCA/PcaTrainer.cs @@ -18,6 +18,7 @@ using Microsoft.ML.Runtime.PCA; using Microsoft.ML.Runtime.Training; using Microsoft.ML.Runtime.Internal.Internallearn; +using Microsoft.ML.Core.Data; [assembly: LoadableClass(RandomizedPcaTrainer.Summary, typeof(RandomizedPcaTrainer), typeof(RandomizedPcaTrainer.Arguments), new[] { typeof(SignatureAnomalyDetectorTrainer), typeof(SignatureTrainer) }, @@ -41,7 +42,7 @@ namespace Microsoft.ML.Runtime.PCA /// /// This PCA can be made into Kernel PCA by using Random Fourier Features transform /// - public sealed class RandomizedPcaTrainer : TrainerBase + public sealed class RandomizedPcaTrainer : TrainerEstimatorBase, PcaPredictor> { public const string LoadNameValue = "pcaAnomaly"; internal const string UserNameValue = "PCA Anomaly Detector"; @@ -73,6 +74,7 @@ public class Arguments : UnsupervisedLearnerInputBaseWithWeight private readonly int _oversampling; private readonly bool _center; private readonly int _seed; + private readonly string _featureColumn; public override PredictionKind PredictionKind => PredictionKind.AnomalyDetection; @@ -80,21 +82,48 @@ public class Arguments : UnsupervisedLearnerInputBaseWithWeight private static readonly TrainerInfo _info = new TrainerInfo(caching: false); public override TrainerInfo Info => _info; - public RandomizedPcaTrainer(IHostEnvironment env, Arguments args) - : base(env, LoadNameValue) + public RandomizedPcaTrainer(IHostEnvironment env, string featureColumn, string weightColumn = null, + int rank = 20, int oversampling = 20, bool center = true, int? seed = null) + : this(env, null, featureColumn, weightColumn, rank, oversampling, center, seed) { - Host.CheckValue(args, nameof(args)); - Host.CheckUserArg(args.Rank > 0, nameof(args.Rank), "Rank must be positive"); - Host.CheckUserArg(args.Oversampling >= 0, nameof(args.Oversampling), "Oversampling must be non-negative"); - - _rank = args.Rank; - _center = args.Center; - _oversampling = args.Oversampling; - _seed = args.Seed ?? Host.Rand.Next(); + + } + + internal RandomizedPcaTrainer(IHostEnvironment env, Arguments args) + :this(env, args, args.FeatureColumn, args.WeightColumn) + { + + } + + private RandomizedPcaTrainer(IHostEnvironment env, Arguments args, string featureColumn, string weightColumn, + int rank = 20, int oversampling = 20, bool center = true, int? seed = null) + : base(Contracts.CheckRef(env, nameof(env)).Register(LoadNameValue), MakeFeatureColumn(featureColumn), null, MakeWeightColumn(weightColumn)) + { + // if the args are not null, we got here from maml, and the internal ctor. + if (args != null) + { + _rank = args.Rank; + _center = args.Center; + _oversampling = args.Oversampling; + _seed = args.Seed ?? Host.Rand.Next(); + } + else + { + _rank = rank; + _center = center; + _oversampling = oversampling; + _seed = seed ?? Host.Rand.Next(); + } + + _featureColumn = featureColumn; + + Host.CheckUserArg(_rank > 0, nameof(_rank), "Rank must be positive"); + Host.CheckUserArg(_oversampling >= 0, nameof(_oversampling), "Oversampling must be non-negative"); + } //Note: the notations used here are the same as in https://web.stanford.edu/group/mmds/slides2010/Martinsson.pdf (pg. 9) - public override PcaPredictor Train(TrainContext context) + protected override PcaPredictor TrainModelCore(TrainContext context) { Host.CheckValue(context, nameof(context)); @@ -108,6 +137,18 @@ public override PcaPredictor Train(TrainContext context) } } + private static SchemaShape.Column MakeWeightColumn(string weightColumn) + { + if (weightColumn == null) + return null; + return new SchemaShape.Column(weightColumn, SchemaShape.Column.VectorKind.Scalar, NumberType.R4, false); + } + + private static SchemaShape.Column MakeFeatureColumn(string featureColumn) + { + return new SchemaShape.Column(featureColumn, SchemaShape.Column.VectorKind.Vector, NumberType.R4, false); + } + private PcaPredictor TrainCore(IChannel ch, RoleMappedData data, int dimension) { Host.AssertValue(ch); @@ -266,6 +307,27 @@ private static void PostProcess(VBuffer[] y, Float[] sigma, Float[] z, in } } + protected override SchemaShape.Column[] GetOutputColumnsCore(SchemaShape inputSchema) + { + return new[] + { + new SchemaShape.Column(DefaultColumnNames.Score, + SchemaShape.Column.VectorKind.Scalar, + NumberType.R4, + false, + new SchemaShape(MetadataUtils.GetTrainerOutputMetadata())), + + new SchemaShape.Column(DefaultColumnNames.PredictedLabel, + SchemaShape.Column.VectorKind.Scalar, + BoolType.Instance, + false, + new SchemaShape(MetadataUtils.GetTrainerOutputMetadata())) + }; + } + + protected override BinaryPredictionTransformer MakeTransformer(PcaPredictor model, ISchema trainSchema) + => new BinaryPredictionTransformer(Host, model, trainSchema, _featureColumn); + [TlcModule.EntryPoint(Name = "Trainers.PcaAnomalyDetector", Desc = "Train an PCA Anomaly model.", UserName = UserNameValue, diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs b/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs index dc28fccc97..18aa400b28 100644 --- a/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs +++ b/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs @@ -5,6 +5,7 @@ using Microsoft.ML.Core.Data; using Microsoft.ML.Runtime.Data; using Microsoft.ML.Runtime.Learners; +using Microsoft.ML.Runtime.PCA; using Microsoft.ML.Runtime.RunTests; using Xunit; using Xunit.Abstractions; @@ -16,5 +17,31 @@ public partial class TrainerEstimators : TestDataPipeBase public TrainerEstimators(ITestOutputHelper helper) : base(helper) { } + + /// + /// FastTreeBinaryClassification TrainerEstimator test + /// + [Fact] + public void PCATrainerEstimator() + { + string featureColumn = "NumericFeatures"; + + var reader = new TextLoader(Env, new TextLoader.Arguments() + { + HasHeader = true, + Separator = "\t", + Column = new[] + { + new TextLoader.Column(featureColumn, DataKind.R4, new [] { new TextLoader.Range(1, 784) }) + } + }); + var data = reader.Read(new MultiFileSource(GetDataPath(TestDatasets.mnistOneClass.trainFilename))); + + + // Pipeline. + var pipeline = new RandomizedPcaTrainer(Env, featureColumn, rank:10); + + TestEstimatorCore(pipeline, data); + } } } From c1d54f79de499475a23bfc155eefcc879dddd581 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Sat, 22 Sep 2018 23:01:03 -0700 Subject: [PATCH 2/8] dehydrate --- .../Scorers/PredictionTransformer.cs | 130 ++++++------------ ...FieldAwareFactorizationMachinePredictor.cs | 29 +--- 2 files changed, 50 insertions(+), 109 deletions(-) diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index 51985a772b..b7f69b89a7 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -27,7 +27,9 @@ namespace Microsoft.ML.Runtime.Data /// Base class for transformers with no feature column, or more than one feature columns. /// /// - public abstract class PredictionTransformerBase : IPredictionTransformer + /// The Scorer used by this + public abstract class PredictionTransformerBase : IPredictionTransformer + where TScorer : RowToRowScorerBase where TModel : class, IPredictor { /// @@ -41,7 +43,9 @@ public abstract class PredictionTransformerBase : IPredictionTransformer protected ISchemaBindableMapper BindableMapper; protected ISchema TrainSchema; - public abstract bool IsRowToRowMapper { get; } + public bool IsRowToRowMapper => true; + + protected abstract TScorer Scorer { get; set; } protected PredictionTransformerBase(IHost host, TModel model, ISchema trainSchema) { @@ -55,7 +59,6 @@ protected PredictionTransformerBase(IHost host, TModel model, ISchema trainSchem } protected PredictionTransformerBase(IHost host, ModelLoadContext ctx) - { Host = host; @@ -91,9 +94,23 @@ protected PredictionTransformerBase(IHost host, ModelLoadContext ctx) /// /// The input data. /// The transformed - public abstract IDataView Transform(IDataView input); - public abstract IRowToRowMapper GetRowToRowMapper(ISchema inputSchema); + public IDataView Transform(IDataView input) + { + Host.CheckValue(input, nameof(input)); + return Scorer.ApplyToData(Host, input); + } + + /// + /// Gets a IRowToRowMapper instance. + /// + /// + /// + public IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) + { + Host.CheckValue(inputSchema, nameof(inputSchema)); + return (IRowToRowMapper)Scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); + } protected void SaveModel(ModelSaveContext ctx) { @@ -118,8 +135,10 @@ protected void SaveModel(ModelSaveContext ctx) /// Those are all the transformers that work with one feature column. /// /// The model used to transform the data. - public abstract class SingleFeaturePredictionTransformerBase : PredictionTransformerBase, ISingleFeaturePredictionTransformer, ICanSaveModel + /// The scorer used on this PredictionTransformer. + public abstract class SingleFeaturePredictionTransformerBase : PredictionTransformerBase, ISingleFeaturePredictionTransformer, ICanSaveModel where TModel : class, IPredictor + where TScorer: RowToRowScorerBase { /// /// The name of the feature column used by the prediction transformer. @@ -131,6 +150,8 @@ public abstract class SingleFeaturePredictionTransformerBase : Predictio /// public ColumnType FeatureColumnType { get; } + protected override TScorer Scorer { get; set; } + public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema trainSchema, string featureColumn) : base(host, model, trainSchema) { @@ -189,17 +210,21 @@ protected virtual void SaveCore(ModelSaveContext ctx) SaveModel(ctx); ctx.SaveStringOrNull(FeatureColumn); } + + protected GenericScorer GetGenericScorer() + { + var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); + return new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); + } } /// /// Base class for the working on binary classification tasks. /// /// An implementation of the - public sealed class BinaryPredictionTransformer : SingleFeaturePredictionTransformerBase + public sealed class BinaryPredictionTransformer : SingleFeaturePredictionTransformerBase where TModel : class, IPredictorProducing { - private readonly BinaryClassifierScorer _scorer; - public readonly string ThresholdColumn; public readonly float Threshold; @@ -213,7 +238,7 @@ public BinaryPredictionTransformer(IHostEnvironment env, TModel model, ISchema i ThresholdColumn = thresholdColumn; var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn }; - _scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); } public BinaryPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) @@ -229,21 +254,7 @@ public BinaryPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn }; - _scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); - } - - public override IDataView Transform(IDataView input) - { - Host.CheckValue(input, nameof(input)); - return _scorer.ApplyToData(Host, input); - } - - public override bool IsRowToRowMapper => true; - - public override IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) - { - Host.CheckValue(inputSchema, nameof(inputSchema)); - return (IRowToRowMapper)_scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); + Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); } protected override void SaveCore(ModelSaveContext ctx) @@ -276,10 +287,9 @@ private static VersionInfo GetVersionInfo() /// Base class for the working on multi-class classification tasks. /// /// An implementation of the - public sealed class MulticlassPredictionTransformer : SingleFeaturePredictionTransformerBase + public sealed class MulticlassPredictionTransformer : SingleFeaturePredictionTransformerBase where TModel : class, IPredictorProducing> { - private readonly MultiClassClassifierScorer _scorer; private readonly string _trainLabelColumn; public MulticlassPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn, string labelColumn) @@ -290,7 +300,7 @@ public MulticlassPredictionTransformer(IHostEnvironment env, TModel model, ISche _trainLabelColumn = labelColumn; var schema = new RoleMappedSchema(inputSchema, labelColumn, featureColumn); var args = new MultiClassClassifierScorer.Arguments(); - _scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); } public MulticlassPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) @@ -304,21 +314,7 @@ public MulticlassPredictionTransformer(IHostEnvironment env, ModelLoadContext ct var schema = new RoleMappedSchema(TrainSchema, _trainLabelColumn, FeatureColumn); var args = new MultiClassClassifierScorer.Arguments(); - _scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); - } - - public override IDataView Transform(IDataView input) - { - Host.CheckValue(input, nameof(input)); - return _scorer.ApplyToData(Host, input); - } - - public override bool IsRowToRowMapper => true; - - public override IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) - { - Host.CheckValue(inputSchema, nameof(inputSchema)); - return (IRowToRowMapper)_scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); + Scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); } protected override void SaveCore(ModelSaveContext ctx) @@ -349,37 +345,19 @@ private static VersionInfo GetVersionInfo() /// Base class for the working on regression tasks. /// /// An implementation of the - public sealed class RegressionPredictionTransformer : SingleFeaturePredictionTransformerBase + public sealed class RegressionPredictionTransformer : SingleFeaturePredictionTransformerBase where TModel : class, IPredictorProducing { - private readonly GenericScorer _scorer; - public RegressionPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), model, inputSchema, featureColumn) { - var schema = new RoleMappedSchema(inputSchema, null, featureColumn); - _scorer = new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = GetGenericScorer(); } internal RegressionPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), ctx) { - var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); - _scorer = new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); - } - - public override IDataView Transform(IDataView input) - { - Host.CheckValue(input, nameof(input)); - return _scorer.ApplyToData(Host, input); - } - - public override bool IsRowToRowMapper => true; - - public override IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) - { - Host.CheckValue(inputSchema, nameof(inputSchema)); - return (IRowToRowMapper)_scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); + Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) @@ -403,37 +381,19 @@ private static VersionInfo GetVersionInfo() } } - public sealed class RankingPredictionTransformer : SingleFeaturePredictionTransformerBase + public sealed class RankingPredictionTransformer : SingleFeaturePredictionTransformerBase where TModel : class, IPredictorProducing { - private readonly GenericScorer _scorer; - public RankingPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), model, inputSchema, featureColumn) { - var schema = new RoleMappedSchema(inputSchema, null, featureColumn); - _scorer = new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = GetGenericScorer(); } internal RankingPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), ctx) { - var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); - _scorer = new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); - } - - public override IDataView Transform(IDataView input) - { - Host.CheckValue(input, nameof(input)); - return _scorer.ApplyToData(Host, input); - } - - public override bool IsRowToRowMapper => true; - - public override IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) - { - Host.CheckValue(inputSchema, nameof(inputSchema)); - return (IRowToRowMapper)_scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); + Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) diff --git a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachinePredictor.cs b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachinePredictor.cs index 6f60ac8d45..e3e9dce5f1 100644 --- a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachinePredictor.cs +++ b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachinePredictor.cs @@ -189,7 +189,7 @@ internal void CopyLatentWeightsTo(AlignedArray latentWeights) } } - public sealed class FieldAwareFactorizationMachinePredictionTransformer : PredictionTransformerBase, ICanSaveModel + public sealed class FieldAwareFactorizationMachinePredictionTransformer : PredictionTransformerBase, ICanSaveModel { public const string LoaderSignature = "FAFMPredXfer"; @@ -203,7 +203,7 @@ public sealed class FieldAwareFactorizationMachinePredictionTransformer : Predic /// public ColumnType[] FeatureColumnTypes { get; } - private readonly BinaryClassifierScorer _scorer; + protected override BinaryClassifierScorer Scorer { get; set; } private readonly string _thresholdColumn; private readonly float _threshold; @@ -235,7 +235,7 @@ public FieldAwareFactorizationMachinePredictionTransformer(IHostEnvironment host var schema = GetSchema(); var args = new BinaryClassifierScorer.Arguments { Threshold = _threshold, ThresholdColumn = _thresholdColumn }; - _scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, trainSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, trainSchema), BindableMapper.Bind(Host, schema), schema); } public FieldAwareFactorizationMachinePredictionTransformer(IHostEnvironment host, ModelLoadContext ctx) @@ -268,11 +268,11 @@ public FieldAwareFactorizationMachinePredictionTransformer(IHostEnvironment host var schema = GetSchema(); var args = new BinaryClassifierScorer.Arguments { Threshold = _threshold, ThresholdColumn = _thresholdColumn }; - _scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); + Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); } /// - /// Gets the result after applying . + /// Gets the result after transformation. /// /// The of the input data. /// The post transformation . @@ -291,25 +291,6 @@ public override ISchema GetOutputSchema(ISchema inputSchema) return Transform(new EmptyDataView(Host, inputSchema)).Schema; } - /// - /// Applies the transformer to the , scoring it through the . - /// - /// The data to be scored with the . - /// The scored . - public override IDataView Transform(IDataView input) - { - Host.CheckValue(input, nameof(input)); - return _scorer.ApplyToData(Host, input); - } - - public override bool IsRowToRowMapper => true; - - public override IRowToRowMapper GetRowToRowMapper(ISchema inputSchema) - { - Host.CheckValue(inputSchema, nameof(inputSchema)); - return (IRowToRowMapper)_scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema)); - } - /// /// Saves the transformer to file. /// From 9d5783d322870f04ba6d9695193ccb9c5a9524b9 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Mon, 24 Sep 2018 11:04:21 -0700 Subject: [PATCH 3/8] tidying the PredictionTransformer class. --- .../Scorers/PredictionTransformer.cs | 36 ++++++++++++------- src/Microsoft.ML.PCA/PcaTrainer.cs | 12 ++++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index b7f69b89a7..942db16177 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -50,11 +50,12 @@ public abstract class PredictionTransformerBase : IPredictionTr protected PredictionTransformerBase(IHost host, TModel model, ISchema trainSchema) { Contracts.CheckValue(host, nameof(host)); - Host = host; - Host.CheckValue(trainSchema, nameof(trainSchema)); + Host.CheckValue(trainSchema, nameof(trainSchema)); Model = model; + + Host.CheckValue(trainSchema, nameof(trainSchema)); TrainSchema = trainSchema; } @@ -152,6 +153,13 @@ public abstract class SingleFeaturePredictionTransformerBase : protected override TScorer Scorer { get; set; } + /// + /// Initializes a new reference of . + /// + /// The local instance of . + /// The model used for scoring. + /// The schema of the training data. + /// The feature column name. public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema trainSchema, string featureColumn) : base(host, model, trainSchema) { @@ -166,6 +174,8 @@ public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema FeatureColumnType = trainSchema.GetColumnType(col); BindableMapper = ScoreUtils.GetSchemaBindableMapper(Host, model); + + GetScorer(); } internal SingleFeaturePredictionTransformerBase(IHost host, ModelLoadContext ctx) @@ -211,7 +221,7 @@ protected virtual void SaveCore(ModelSaveContext ctx) ctx.SaveStringOrNull(FeatureColumn); } - protected GenericScorer GetGenericScorer() + protected virtual GenericScorer GetScorer() { var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); return new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); @@ -233,12 +243,10 @@ public BinaryPredictionTransformer(IHostEnvironment env, TModel model, ISchema i : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(BinaryPredictionTransformer)), model, inputSchema, featureColumn) { Host.CheckNonEmpty(thresholdColumn, nameof(thresholdColumn)); - var schema = new RoleMappedSchema(inputSchema, null, featureColumn); Threshold = threshold; ThresholdColumn = thresholdColumn; - var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn }; - Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + SetScorer(); } public BinaryPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) @@ -251,7 +259,11 @@ public BinaryPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) Threshold = ctx.Reader.ReadSingle(); ThresholdColumn = ctx.LoadString(); + SetScorer(); + } + private void SetScorer() + { var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn }; Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); @@ -298,9 +310,7 @@ public MulticlassPredictionTransformer(IHostEnvironment env, TModel model, ISche Host.CheckValueOrNull(labelColumn); _trainLabelColumn = labelColumn; - var schema = new RoleMappedSchema(inputSchema, labelColumn, featureColumn); - var args = new MultiClassClassifierScorer.Arguments(); - Scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, inputSchema), BindableMapper.Bind(Host, schema), schema); + SetScorer(); } public MulticlassPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) @@ -311,7 +321,11 @@ public MulticlassPredictionTransformer(IHostEnvironment env, ModelLoadContext ct // id of string: train label column _trainLabelColumn = ctx.LoadStringOrNull(); + SetScorer(); + } + private void SetScorer() + { var schema = new RoleMappedSchema(TrainSchema, _trainLabelColumn, FeatureColumn); var args = new MultiClassClassifierScorer.Arguments(); Scorer = new MultiClassClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); @@ -351,13 +365,11 @@ public sealed class RegressionPredictionTransformer : SingleFeaturePredi public RegressionPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), model, inputSchema, featureColumn) { - Scorer = GetGenericScorer(); } internal RegressionPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), ctx) { - Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) @@ -387,13 +399,11 @@ public sealed class RankingPredictionTransformer : SingleFeaturePredicti public RankingPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), model, inputSchema, featureColumn) { - Scorer = GetGenericScorer(); } internal RankingPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), ctx) { - Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) diff --git a/src/Microsoft.ML.PCA/PcaTrainer.cs b/src/Microsoft.ML.PCA/PcaTrainer.cs index eb9d242db8..47aa3a56ef 100644 --- a/src/Microsoft.ML.PCA/PcaTrainer.cs +++ b/src/Microsoft.ML.PCA/PcaTrainer.cs @@ -82,6 +82,16 @@ public class Arguments : UnsupervisedLearnerInputBaseWithWeight private static readonly TrainerInfo _info = new TrainerInfo(caching: false); public override TrainerInfo Info => _info; + /// + /// Initializes a new instance of . + /// + /// The local instance of the . + /// The name of the feature column. + /// The name of the weight column. + /// The number of components in the PCA. + /// Oversampling parameter for randomized PCA training. + /// If enabled, data is centered to be zero mean. + /// The seed for random number generation. public RandomizedPcaTrainer(IHostEnvironment env, string featureColumn, string weightColumn = null, int rank = 20, int oversampling = 20, bool center = true, int? seed = null) : this(env, null, featureColumn, weightColumn, rank, oversampling, center, seed) @@ -97,7 +107,7 @@ internal RandomizedPcaTrainer(IHostEnvironment env, Arguments args) private RandomizedPcaTrainer(IHostEnvironment env, Arguments args, string featureColumn, string weightColumn, int rank = 20, int oversampling = 20, bool center = true, int? seed = null) - : base(Contracts.CheckRef(env, nameof(env)).Register(LoadNameValue), MakeFeatureColumn(featureColumn), null, MakeWeightColumn(weightColumn)) + : base(Contracts.CheckRef(env, nameof(env)).Register(LoadNameValue), TrainerUtils.MakeR4VecFeature(featureColumn), null, TrainerUtils.MakeR4ScalarWeightColumn(weightColumn)) { // if the args are not null, we got here from maml, and the internal ctor. if (args != null) From 431cfae88cd0f3fd6b4431af52fb4c00ae436c84 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Mon, 24 Sep 2018 22:18:35 -0700 Subject: [PATCH 4/8] adressing review comments. --- .../Scorers/BinaryClassifierScorer.cs | 2 +- .../Scorers/PredictionTransformer.cs | 94 ++++++++++++++++++- src/Microsoft.ML.PCA/PcaTrainer.cs | 6 +- 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs b/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs index 950a838709..b36dc9f381 100644 --- a/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs +++ b/src/Microsoft.ML.Data/Scorers/BinaryClassifierScorer.cs @@ -297,6 +297,6 @@ private static ColumnType GetPredColType(ColumnType scoreType, ISchemaBoundRowMa } private static bool OutputTypeMatches(ColumnType scoreType) - => scoreType == NumberType.Float; + => scoreType == NumberType.Float; } } diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index 942db16177..c06127b095 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -20,6 +20,9 @@ [assembly: LoadableClass(typeof(RankingPredictionTransformer>), typeof(RankingPredictionTransformer), null, typeof(SignatureLoadModel), "", RankingPredictionTransformer.LoaderSignature)] +[assembly: LoadableClass(typeof(AnomalyPredictionTransformer>), typeof(AnomalyPredictionTransformer), null, typeof(SignatureLoadModel), + "", AnomalyPredictionTransformer.LoaderSignature)] + namespace Microsoft.ML.Runtime.Data { @@ -174,8 +177,6 @@ public SingleFeaturePredictionTransformerBase(IHost host, TModel model, ISchema FeatureColumnType = trainSchema.GetColumnType(col); BindableMapper = ScoreUtils.GetSchemaBindableMapper(Host, model); - - GetScorer(); } internal SingleFeaturePredictionTransformerBase(IHost host, ModelLoadContext ctx) @@ -221,13 +222,80 @@ protected virtual void SaveCore(ModelSaveContext ctx) ctx.SaveStringOrNull(FeatureColumn); } - protected virtual GenericScorer GetScorer() + protected virtual GenericScorer GetGenericScorer() { var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); return new GenericScorer(Host, new GenericScorer.Arguments(), new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); } } + /// + /// Base class for the working on anomaly detection tasks. + /// + /// An implementation of the + public sealed class AnomalyPredictionTransformer : SingleFeaturePredictionTransformerBase + where TModel : class, IPredictorProducing + { + public readonly string ThresholdColumn; + public readonly float Threshold; + + public AnomalyPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn, + float threshold = 0f, string thresholdColumn = DefaultColumnNames.Score) + : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(BinaryPredictionTransformer)), model, inputSchema, featureColumn) + { + Host.CheckNonEmpty(thresholdColumn, nameof(thresholdColumn)); + Threshold = threshold; + ThresholdColumn = thresholdColumn; + + SetScorer(); + } + + public AnomalyPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) + : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(BinaryPredictionTransformer)), ctx) + { + // *** Binary format *** + // + // float: scorer threshold + // id of string: scorer threshold column + + Threshold = ctx.Reader.ReadSingle(); + ThresholdColumn = ctx.LoadString(); + SetScorer(); + } + + private void SetScorer() + { + var schema = new RoleMappedSchema(TrainSchema, null, FeatureColumn); + var args = new BinaryClassifierScorer.Arguments { Threshold = Threshold, ThresholdColumn = ThresholdColumn }; + Scorer = new BinaryClassifierScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema); + } + + protected override void SaveCore(ModelSaveContext ctx) + { + Contracts.AssertValue(ctx); + ctx.SetVersionInfo(GetVersionInfo()); + + // *** Binary format *** + // + // float: scorer threshold + // id of string: scorer threshold column + base.SaveCore(ctx); + + ctx.Writer.Write(Threshold); + ctx.SaveString(ThresholdColumn); + } + + private static VersionInfo GetVersionInfo() + { + return new VersionInfo( + modelSignature: "ANOMPRED", + verWrittenCur: 0x00010001, // Initial + verReadableCur: 0x00010001, + verWeCanReadBack: 0x00010001, + loaderSignature: AnomalyPredictionTransformer.LoaderSignature); + } + } + /// /// Base class for the working on binary classification tasks. /// @@ -365,11 +433,13 @@ public sealed class RegressionPredictionTransformer : SingleFeaturePredi public RegressionPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), model, inputSchema, featureColumn) { + Scorer = GetGenericScorer(); } internal RegressionPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RegressionPredictionTransformer)), ctx) { + Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) @@ -385,7 +455,7 @@ protected override void SaveCore(ModelSaveContext ctx) private static VersionInfo GetVersionInfo() { return new VersionInfo( - modelSignature: "MC PRED", + modelSignature: "REG PRED", verWrittenCur: 0x00010001, // Initial verReadableCur: 0x00010001, verWeCanReadBack: 0x00010001, @@ -393,17 +463,23 @@ private static VersionInfo GetVersionInfo() } } + /// + /// Base class for the working on ranking tasks. + /// + /// An implementation of the public sealed class RankingPredictionTransformer : SingleFeaturePredictionTransformerBase where TModel : class, IPredictorProducing { public RankingPredictionTransformer(IHostEnvironment env, TModel model, ISchema inputSchema, string featureColumn) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), model, inputSchema, featureColumn) { + Scorer = GetGenericScorer(); } internal RankingPredictionTransformer(IHostEnvironment env, ModelLoadContext ctx) : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(RankingPredictionTransformer)), ctx) { + Scorer = GetGenericScorer(); } protected override void SaveCore(ModelSaveContext ctx) @@ -419,7 +495,7 @@ protected override void SaveCore(ModelSaveContext ctx) private static VersionInfo GetVersionInfo() { return new VersionInfo( - modelSignature: "MC RANK", + modelSignature: "RANK PRED", verWrittenCur: 0x00010001, // Initial verReadableCur: 0x00010001, verWeCanReadBack: 0x00010001, @@ -458,4 +534,12 @@ internal static class RankingPredictionTransformer public static RankingPredictionTransformer> Create(IHostEnvironment env, ModelLoadContext ctx) => new RankingPredictionTransformer>(env, ctx); } + + internal static class AnomalyPredictionTransformer + { + public const string LoaderSignature = "AnomalyPredXfer"; + + public static AnomalyPredictionTransformer> Create(IHostEnvironment env, ModelLoadContext ctx) + => new AnomalyPredictionTransformer>(env, ctx); + } } diff --git a/src/Microsoft.ML.PCA/PcaTrainer.cs b/src/Microsoft.ML.PCA/PcaTrainer.cs index 47aa3a56ef..5a2854bc5c 100644 --- a/src/Microsoft.ML.PCA/PcaTrainer.cs +++ b/src/Microsoft.ML.PCA/PcaTrainer.cs @@ -42,7 +42,7 @@ namespace Microsoft.ML.Runtime.PCA /// /// This PCA can be made into Kernel PCA by using Random Fourier Features transform /// - public sealed class RandomizedPcaTrainer : TrainerEstimatorBase, PcaPredictor> + public sealed class RandomizedPcaTrainer : TrainerEstimatorBase, PcaPredictor> { public const string LoadNameValue = "pcaAnomaly"; internal const string UserNameValue = "PCA Anomaly Detector"; @@ -335,8 +335,8 @@ protected override SchemaShape.Column[] GetOutputColumnsCore(SchemaShape inputSc }; } - protected override BinaryPredictionTransformer MakeTransformer(PcaPredictor model, ISchema trainSchema) - => new BinaryPredictionTransformer(Host, model, trainSchema, _featureColumn); + protected override AnomalyPredictionTransformer MakeTransformer(PcaPredictor model, ISchema trainSchema) + => new AnomalyPredictionTransformer(Host, model, trainSchema, _featureColumn); [TlcModule.EntryPoint(Name = "Trainers.PcaAnomalyDetector", Desc = "Train an PCA Anomaly model.", From 43f5775b642d39d815001a7caa3837ecf343505f Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Tue, 25 Sep 2018 13:54:20 -0700 Subject: [PATCH 5/8] merging from master --- src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs | 3 ++- updateMe.bat | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 updateMe.bat diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index 0b54a3fcca..3e9208ff5b 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -292,7 +292,8 @@ private static VersionInfo GetVersionInfo() verWrittenCur: 0x00010001, // Initial verReadableCur: 0x00010001, verWeCanReadBack: 0x00010001, - loaderSignature: AnomalyPredictionTransformer.LoaderSignature); + loaderSignature: AnomalyPredictionTransformer.LoaderSignature, + loaderAssemblyName: typeof(AnomalyPredictionTransformer<>).Assembly.FullName); } } diff --git a/updateMe.bat b/updateMe.bat new file mode 100644 index 0000000000..f3d089be67 --- /dev/null +++ b/updateMe.bat @@ -0,0 +1,5 @@ +::Update the https://github.com/sfilipi/machinelearning-1 from https://github.com/dotnet/machinelearning +git fetch upstream +git checkout master +git merge upstream/master +git push -u origin master \ No newline at end of file From eac7be4f8e1a8834557993e9d6a944382c8526d2 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Tue, 25 Sep 2018 13:55:06 -0700 Subject: [PATCH 6/8] Revert "merging from master" This reverts commit 43f5775b642d39d815001a7caa3837ecf343505f. --- src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs | 3 +-- updateMe.bat | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 updateMe.bat diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index 3e9208ff5b..0b54a3fcca 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -292,8 +292,7 @@ private static VersionInfo GetVersionInfo() verWrittenCur: 0x00010001, // Initial verReadableCur: 0x00010001, verWeCanReadBack: 0x00010001, - loaderSignature: AnomalyPredictionTransformer.LoaderSignature, - loaderAssemblyName: typeof(AnomalyPredictionTransformer<>).Assembly.FullName); + loaderSignature: AnomalyPredictionTransformer.LoaderSignature); } } diff --git a/updateMe.bat b/updateMe.bat deleted file mode 100644 index f3d089be67..0000000000 --- a/updateMe.bat +++ /dev/null @@ -1,5 +0,0 @@ -::Update the https://github.com/sfilipi/machinelearning-1 from https://github.com/dotnet/machinelearning -git fetch upstream -git checkout master -git merge upstream/master -git push -u origin master \ No newline at end of file From f1a12130538e2b026ec64211ede7362d17f7e791 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Tue, 25 Sep 2018 13:57:14 -0700 Subject: [PATCH 7/8] post master merge fix. --- src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs index 0b54a3fcca..3e9208ff5b 100644 --- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs +++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs @@ -292,7 +292,8 @@ private static VersionInfo GetVersionInfo() verWrittenCur: 0x00010001, // Initial verReadableCur: 0x00010001, verWeCanReadBack: 0x00010001, - loaderSignature: AnomalyPredictionTransformer.LoaderSignature); + loaderSignature: AnomalyPredictionTransformer.LoaderSignature, + loaderAssemblyName: typeof(AnomalyPredictionTransformer<>).Assembly.FullName); } } From 65c1ee379d9761b08eb1bb72b90c195fc5ea4027 Mon Sep 17 00:00:00 2001 From: Senja Filipi Date: Tue, 25 Sep 2018 14:05:42 -0700 Subject: [PATCH 8/8] Call Done() when test is done. --- test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs b/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs index bf5f951617..dcde5e65aa 100644 --- a/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs +++ b/test/Microsoft.ML.Tests/TrainerEstimators/TrainerEstimators.cs @@ -42,6 +42,7 @@ public void PCATrainerEstimator() var pipeline = new RandomizedPcaTrainer(Env, featureColumn, rank:10); TestEstimatorCore(pipeline, data); + Done(); } private (IEstimator, IDataView) GetBinaryClassificationPipeline()