From bf9d946227c2180b63661a417c15c6cfc909bd14 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Thu, 7 Feb 2019 16:00:25 -0800
Subject: [PATCH 01/23] Updated docs for AveragedPerceptron

---
 .../Standard/Online/AveragedLinear.cs         | 35 +++++++++++++++++
 .../Standard/Online/AveragedPerceptron.cs     | 19 ++++++---
 .../Standard/Online/OnlineLinear.cs           | 16 ++++++++
 .../Standard/Online/doc.xml                   | 39 -------------------
 .../StandardLearnersCatalog.cs                | 36 +++++++++++++----
 5 files changed, 93 insertions(+), 52 deletions(-)

diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
index bf7a88d27d..0a73a0ac3b 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
@@ -18,36 +18,71 @@ namespace Microsoft.ML.Trainers.Online
 {
     public abstract class AveragedLinearArguments : OnlineLinearArguments
     {
+        /// <summary>
+        /// <a href="tmpurl_lr">Learning rate</a>
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)]
         [TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")]
         [TlcModule.SweepableDiscreteParam("LearningRate", new object[] { 0.01, 0.1, 0.5, 1.0 })]
         public float LearningRate = AveragedDefaultArgs.LearningRate;
 
+        /// <summary>
+        /// <see langword="true" /> to decrease the <a href="tmpurl_lr">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)]
         [TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")]
         [TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })]
         public bool DecreaseLearningRate = AveragedDefaultArgs.DecreaseLearningRate;
 
+        /// <summary>
+        /// Number of examples after which weights will be reset to the current average.
+        /// Default is <see langword="null" />, which disables this feature.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of examples after which weights will be reset to the current average", ShortName = "numreset")]
         public long? ResetWeightsAfterXExamples = null;
 
+        /// <summary>
+        /// <see langword="true" /> to update averaged weights only when loss is nonzero.
+        /// <see langword="false" /> to update averaged weights on every example.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Instead of updating averaged weights on every example, only update when loss is nonzero", ShortName = "lazy")]
         public bool DoLazyUpdates = true;
 
+        /// <summary>
+        /// L2 weight for <a href='tmpurl_regularization'>regularization</a>.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization Weight", ShortName = "reg", SortOrder = 50)]
         [TGUI(Label = "L2 Regularization Weight")]
         [TlcModule.SweepableFloatParam("L2RegularizerWeight", 0.0f, 0.4f)]
         public float L2RegularizerWeight = AveragedDefaultArgs.L2RegularizerWeight;
 
+        /// <summary>
+        /// Extra weight given to more recent updates.
+        /// Default is 0, i.e. no extra gain.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Extra weight given to more recent updates", ShortName = "rg")]
         public float RecencyGain = 0;
 
+        /// <summary>
+        /// <see langword="true" /> means <see cref="RecencyGain"/> is multiplicative.
+        /// <see langword="false" /> means <see cref="RecencyGain"/> is additive.
+        /// Default is <see langword="false" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether Recency Gain is multiplicative (vs. additive)", ShortName = "rgm")]
         public bool RecencyGainMulti = false;
 
+        /// <summary>
+        /// <see langword="true" /> to do averaging; otherwise, <see langword="false" />.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Do averaging?", ShortName = "avg")]
         public bool Averaged = true;
 
+        /// <summary>
+        /// The inexactness tolerance for averaging.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "The inexactness tolerance for averaging", ShortName = "avgtol")]
         public float AveragedTolerance = (float)1e-2;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
index b3659974fa..2e82f8af5e 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
@@ -25,12 +25,10 @@
 
 namespace Microsoft.ML.Trainers.Online
 {
-    // This is an averaged perceptron classifier.
-    // Configurable subcomponents:
-    //     - Loss function. By default, hinge loss (aka max-margin avgd perceptron)
-    //     - Feature normalization. By default, rescaling between min and max values for every feature
-    //     - Prediction calibration to produce probabilities. Off by default, if on, uses exponential (aka Platt) calibration.
-    /// <include file='doc.xml' path='doc/members/member[@name="AP"]/*' />
+    /// <summary>
+    /// This is averaged perceptron trainer.
+    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+    /// </summary>
     public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPredictionTransformer<LinearBinaryModelParameters>, LinearBinaryModelParameters>
     {
         public const string LoadNameValue = "AveragedPerceptron";
@@ -42,12 +40,21 @@ public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPred
 
         public sealed class Options : AveragedLinearArguments
         {
+            /// <summary>
+            /// The custom <a href="tmpurl_loss">loss</a>. Default is hinge loss.
+            /// </summary>
             [Argument(ArgumentType.Multiple, HelpText = "Loss Function", ShortName = "loss", SortOrder = 50)]
             public ISupportClassificationLossFactory LossFunction = new HingeLoss.Arguments();
 
+            /// <summary>
+            /// The <a href="tmpurl_calib">calibrator</a> for producing probabilities. Default is exponential (aka Platt) calibration.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
             public ICalibratorTrainerFactory Calibrator = new PlattCalibratorTrainerFactory();
 
+            /// <summary>
+            /// The maximum number of examples to use when training the calibrator.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "The maximum number of examples to use when training the calibrator", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
             public int MaxCalibrationExamples = 1000000;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
index 2128c8e008..4e1cb6e341 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
@@ -20,24 +20,40 @@ namespace Microsoft.ML.Trainers.Online
 
     public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
     {
+        /// <summary>
+        /// Number of training iterations through the data.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter", SortOrder = 50)]
         [TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")]
         [TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)]
         public int NumIterations = OnlineDefaultArgs.NumIterations;
 
+        /// <summary>
+        /// Initial weights and bias, comma-separated.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Initial Weights and bias, comma-separated", ShortName = "initweights")]
         [TGUI(NoSweep = true)]
         public string InitialWeights;
 
+        /// <summary>
+        /// Initial weights scale.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts", SortOrder = 140)]
         [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
         [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
         public float InitWtsDiameter = 0;
 
+        /// <summary>
+        /// <see langword="true" /> to shuffle data for each training iteration; otherwise, <see langword="false" />.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether to shuffle for each training iteration", ShortName = "shuf")]
         [TlcModule.SweepableDiscreteParamAttribute("Shuffle", new object[] { false, true })]
         public bool Shuffle = true;
 
+        /// <summary>
+        /// Size of cache when trained in Scope.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Size of cache when trained in Scope", ShortName = "cache")]
         public int StreamingCacheSize = 1000000;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml b/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
index 8e8f5dc2ba..292aeface5 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
@@ -25,44 +25,5 @@
         </code>
       </example>
     </example>
-
-    <member name="AP">
-      <summary>
-        Averaged Perceptron Binary Classifier. 
-      </summary>
-      <remarks>
-        Perceptron is a classification algorithm that makes its predictions based on a linear function.
-        I.e., for an instance with feature values f0, f1,..., f_D-1, , the prediction is given by the sign of sigma[0,D-1] ( w_i * f_i), where w_0, w_1,...,w_D-1 are the weights computed by the algorithm.
-        <para>
-          Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
-          The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
-          If this value has the same sign as the label of the current example, the weights remain the same. If they have opposite signs,
-          the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
-          multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate. In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
-          and by the gradient of some loss function (in the specific case described above, the loss is hinge-loss, whose gradient is 1 when it is non-zero).
-        </para>
-        <para>
-          In Averaged Perceptron (AKA voted-perceptron), the weight vectors are stored,
-          together with a weight that counts the number of iterations it survived (this is equivalent to storing the weight vector after every iteration, regardless of whether it was updated or not).
-          The prediction is then calculated by taking the weighted average of all the sums sigma[0, D-1] (w_i * f_i) or the different weight vectors.
-        </para>
-        <para> For more information see:</para>
-        <para><a href='https://en.wikipedia.org/wiki/Perceptron'>Wikipedia entry for Perceptron</a></para>
-        <para><a href='https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.48.8200'>Large Margin Classification Using the Perceptron Algorithm</a></para>
-      </remarks>
-    </member>
-    <example>
-      <example name="AP">
-        <code language="csharp">
-          new AveragedPerceptronBinaryClassifier
-          {
-            NumIterations = 10,
-            L2RegularizerWeight = 0.01f,
-            LossFunction = new ExpLossClassificationLossFunction()
-          }
-        </code>
-      </example>
-    </example>
-
   </members>
 </doc>
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 6442e4eb01..dd0ffc76c0 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -191,16 +191,37 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         }
 
         /// <summary>
-        /// Predict a target using a linear binary classification model trained with the AveragedPerceptron trainer.
+        /// Predict a target using a linear binary classification model trained with averaged perceptron trainer.
         /// </summary>
+        /// <remarks>
+        /// Perceptron is a classification algorithm that makes its predictions based on a linear function.
+        /// For instance with feature values f0, f1,..., f_D-1, the prediction is given by the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
+        ///
+        /// Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
+        /// The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
+        /// If this value has the same sign as the label of the current example, the weights remain the same.If they have opposite signs,
+        /// the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
+        /// multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate.In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
+        /// and by the gradient of some loss function (in the specific case described above, the loss is hinge-loss, whose gradient is 1 when it is non-zero).
+        ///
+        /// In Averaged Perceptron (AKA voted-perceptron), the weight vectors are stored,
+        /// together with a weight that counts the number of iterations it survived (this is equivalent to storing the weight vector after every iteration, regardless of whether it was updated or not).
+        /// The prediction is then calculated by taking the weighted average of all the sums sigma[0, D-1] (w_i * f_i) or the different weight vectors.
+        ///
+        /// For more information see <a href="https://en.wikipedia.org/wiki/Perceptron">Wikipedia entry for Perceptron</a>
+        /// or <a href="https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.48.8200">Large Margin Classification Using the Perceptron Algorithm</a>
+        /// </remarks>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="labelColumn">The name of the label column, or dependent variable.</param>
         /// <param name="featureColumn">The features, or independent variables.</param>
-        /// <param name="lossFunction">The custom loss.</param>
+        /// <param name="lossFunction">The custom <a href="tmpurl_loss">loss</a>. If <see langword="null"/>, hinge loss will be used resulting in max-margin averaged perceptron.</param>
         /// <param name="weights">The optional example weights.</param>
-        /// <param name="learningRate">The learning Rate.</param>
-        /// <param name="decreaseLearningRate">Decrease learning rate as iterations progress.</param>
-        /// <param name="l2RegularizerWeight">L2 regularization weight.</param>
+        /// <param name="learningRate"><a href="tmpurl_lr">Learning rate</a>.</param>
+        /// <param name="decreaseLearningRate">
+        /// <see langword="true" /> to decrease the <a href="tmpurl_calib">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </param>
+        /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
         /// <param name="numIterations">Number of training iterations through the data.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog,
@@ -220,10 +241,11 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         }
 
         /// <summary>
-        /// Predict a target using a linear binary classification model trained with the AveragedPerceptron trainer.
+        /// Predict a target using a linear binary classification model trained with averaged perceptron trainer using advanced options.
+        /// For trainer details, please see the remarks for <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
-        /// <param name="options">Advanced arguments to the algorithm.</param>
+        /// <param name="options">Advanced trainer options.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {

From 4222e85005663c696b96315ee0d2d0e8d84591e6 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Fri, 8 Feb 2019 14:29:34 -0800
Subject: [PATCH 02/23] Added a sample

---
 .../AveragedPerceptron.cs                     | 61 +++++++++++++++++++
 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs | 21 +++++++
 .../SamplesDatasetUtils.cs                    | 31 ++++++++++
 .../StandardLearnersCatalog.cs                |  9 ++-
 4 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
 create mode 100644 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
new file mode 100644
index 0000000000..8871711b61
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
@@ -0,0 +1,61 @@
+using Microsoft.ML;
+
+namespace Microsoft.ML.Samples.Dynamic.BinaryClassification
+{
+    public static class AveragedPerceptron
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this examples to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download the dataset and load it as IDataView
+            var data = SamplesUtils.DatasetUtils.LoadAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Create data processing pipeline
+            var pipeline =
+                // Convert categorical features to one-hot vectors
+                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
+                // Combine all features into one feature vector
+                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
+                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num", 
+                    "capital-gain", "capital-loss", "hours-per-week"))
+                // Min-max normalized all the features
+                .Append(mlContext.Transforms.Normalize("Features"))
+                // Add the trainer
+                .Append(mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features"));
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintBinaryClassificationMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.85
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.91
+            // Positive Precision: 0.69
+            // Positive Recall: 0.63
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
new file mode 100644
index 0000000000..814a251d6a
--- /dev/null
+++ b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.ML.Data;
+
+namespace Microsoft.ML.SamplesUtils
+{
+    public static class ConsoleUtils
+    {
+        public static void PrintBinaryClassificationMetrics(BinaryClassificationMetrics metrics)
+        {
+            Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
+            Console.WriteLine($"AUC: {metrics.Auc:F2}");
+            Console.WriteLine($"F1 Score: {metrics.F1Score:F2}");
+            Console.WriteLine($"Negative Precision: {metrics.NegativePrecision:F2}");
+            Console.WriteLine($"Negative Recall: {metrics.NegativeRecall:F2}");
+            Console.WriteLine($"Positive Precision: {metrics.PositivePrecision:F2}");
+            Console.WriteLine($"Positive Recall: {metrics.PositiveRecall:F2}");
+        }
+    }
+}
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index edaf2d55c5..724f4d10f8 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -81,6 +81,37 @@ public static string DownloadSentimentDataset()
         public static string DownloadAdultDataset()
             => Download("https://raw.githubusercontent.com/dotnet/machinelearning/244a8c2ac832657af282aa312d568211698790aa/test/data/adult.train", "adult.txt");
 
+        public static IDataView LoadAdultDataset(MLContext mlContext)
+        {
+            // Download the file
+            string dataFile = DownloadAdultDataset();
+
+            // Define the columns to read
+            var reader = mlContext.Data.CreateTextLoader(
+                columns: new[]
+                    {
+                        new TextLoader.Column("age", DataKind.R4, 0),
+                        new TextLoader.Column("workclass", DataKind.TX, 1),
+                        new TextLoader.Column("fnlwgt", DataKind.R4, 2),
+                        new TextLoader.Column("education", DataKind.TX, 3),
+                        new TextLoader.Column("education-num", DataKind.R4, 4),
+                        new TextLoader.Column("marital-status", DataKind.TX, 5),
+                        new TextLoader.Column("occupation", DataKind.TX, 6),
+                        new TextLoader.Column("relationship", DataKind.TX, 7),
+                        new TextLoader.Column("ethnicity", DataKind.TX, 8),
+                        new TextLoader.Column("sex", DataKind.TX, 9),
+                        new TextLoader.Column("capital-gain", DataKind.R4, 10),
+                        new TextLoader.Column("capital-loss", DataKind.R4, 11),
+                        new TextLoader.Column("hours-per-week", DataKind.R4, 12),
+                        new TextLoader.Column("native-country", DataKind.R4, 13),
+                        new TextLoader.Column("IsOver50K", DataKind.BL, 14),
+                    },
+                separatorChar: ',',
+                hasHeader: true
+            );
+
+            return reader.Read(dataFile);
+        }
         /// <summary>
         /// Downloads the breast cancer dataset from the ML.NET repo.
         /// </summary>
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index dd0ffc76c0..01bbc7f60a 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -223,6 +223,13 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// </param>
         /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
         /// <param name="numIterations">Number of training iterations through the data.</param>
+        /// <example>
+        /// <format type="text/markdown">
+        /// <![CDATA[
+        /// [!code-csharp[AveragedPerceptron](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs)]
+        /// ]]>
+        /// </format>
+        /// </example>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog,
             string labelColumn = DefaultColumnNames.Label,
@@ -242,7 +249,7 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
 
         /// <summary>
         /// Predict a target using a linear binary classification model trained with averaged perceptron trainer using advanced options.
-        /// For trainer details, please see the remarks for <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+        /// For usage details, please see <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="options">Advanced trainer options.</param>

From 48dffb054776bd536d40124aaf18253e18f3df67 Mon Sep 17 00:00:00 2001
From: Ivan Matantsev <Ivanidze.kirov@gmail.com>
Date: Fri, 8 Feb 2019 15:21:22 -0800
Subject: [PATCH 03/23] Internalize and cleanup recommender project (#2451)

---
 .../Recommendation/MatrixFactorization.cs     |  62 ++++++
 .../MatrixFactorizationWithOptions.cs}        |  69 ++-----
 .../MatrixFactorizationPredictor.cs           | 182 ++++++++++--------
 .../MatrixFactorizationTrainer.cs             | 163 ++++++++++++----
 .../RecommenderCatalog.cs                     |  19 +-
 .../SamplesDatasetUtils.cs                    |  63 +++++-
 .../FactorizationMachineTrainer.cs            |   4 +
 .../MatrixFactorizationStatic.cs              |   2 +-
 .../Training.cs                               |   2 +-
 .../MatrixFactorizationTests.cs               |  35 +++-
 10 files changed, 408 insertions(+), 193 deletions(-)
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs
 rename docs/samples/Microsoft.ML.Samples/Dynamic/{MatrixFactorization.cs => Trainers/Recommendation/MatrixFactorizationWithOptions.cs} (50%)

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs
new file mode 100644
index 0000000000..91de5a8be8
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.ML.Data;
+using static Microsoft.ML.SamplesUtils.DatasetUtils;
+
+namespace Microsoft.ML.Samples.Dynamic
+{
+    public partial class MatrixFactorizationExample
+    {
+        // This example first creates in-memory data and then use it to train a matrix factorization mode with default parameters. Afterward, quality metrics are reported.
+        public static void MatrixFactorization()
+        {
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging,
+            // as a catalog of available operations and as the source of randomness.
+            var mlContext = new MLContext(seed: 0, conc: 1);
+
+            // Get a small in-memory dataset.
+            var data = GetRecommendationData();
+
+            // Convert the in-memory matrix into an IDataView so that ML.NET components can consume it.
+            var dataView = mlContext.Data.ReadFromEnumerable(data);
+
+            // Create a matrix factorization trainer which may consume "Value" as the training label, "MatrixColumnIndex" as the
+            // matrix's column index, and "MatrixRowIndex" as the matrix's row index. Here nameof(...) is used to extract field
+            // names' in MatrixElement class.
+            var pipeline = mlContext.Recommendation().Trainers.MatrixFactorization(nameof(MatrixElement.MatrixColumnIndex),
+                nameof(MatrixElement.MatrixRowIndex), nameof(MatrixElement.Value), 10, 0.2, 10);
+
+            // Train a matrix factorization model.
+            var model = pipeline.Fit(dataView);
+
+            // Apply the trained model to the training set.
+            var prediction = model.Transform(dataView);
+
+            // Calculate regression matrices for the prediction result.
+            var metrics = mlContext.Recommendation().Evaluate(prediction,
+                label: nameof(MatrixElement.Value), score: nameof(MatrixElementForScore.Score));
+
+            // Print out some metrics for checking the model's quality.
+            Console.WriteLine($"L1 - {metrics.L1}"); // 0.17208
+            Console.WriteLine($"L2 - {metrics.L2}"); // 0.04766
+            Console.WriteLine($"LossFunction - {metrics.LossFn}"); // 0.04766
+            Console.WriteLine($"RMS - {metrics.Rms}"); //0.21831
+            Console.WriteLine($"RSquared - {metrics.RSquared}"); // 0.97616
+
+            // Create two two entries for making prediction. Of course, the prediction value, Score, is unknown so it can be anything
+            // (here we use Score=0 and it will be overwritten by the true prediction). If any of row and column indexes are out-of-range
+            // (e.g., MatrixColumnIndex=99999), the prediction value will be NaN.
+            var testMatrix = new List<MatrixElementForScore>() {
+                new MatrixElementForScore() { MatrixColumnIndex = 1, MatrixRowIndex = 7, Score = 0 },
+                new MatrixElementForScore() { MatrixColumnIndex = 3, MatrixRowIndex = 6, Score = 0 } };
+            
+            // Again, convert the test data to a format supported by ML.NET.
+            var testDataView = mlContext.Data.ReadFromEnumerable(testMatrix);
+            // Feed the test data into the model and then iterate through all predictions.
+            foreach (var pred in mlContext.CreateEnumerable<MatrixElementForScore>(model.Transform(testDataView), false))
+                Console.WriteLine($"Predicted value at row {pred.MatrixRowIndex - 1} and column {pred.MatrixColumnIndex - 1} is {pred.Score}");
+            // Predicted value at row 7 and column 1 is 2.876928
+            // Predicted value at row 6 and column 3 is 3.587935
+        }
+    }
+}
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/MatrixFactorization.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorizationWithOptions.cs
similarity index 50%
rename from docs/samples/Microsoft.ML.Samples/Dynamic/MatrixFactorization.cs
rename to docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorizationWithOptions.cs
index 449aa46017..eb776087bf 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/MatrixFactorization.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorizationWithOptions.cs
@@ -2,65 +2,29 @@
 using System.Collections.Generic;
 using Microsoft.ML.Data;
 using Microsoft.ML.Trainers;
+using static Microsoft.ML.SamplesUtils.DatasetUtils;
 
 namespace Microsoft.ML.Samples.Dynamic
 {
-    public class MatrixFactorizationExample
+    public partial class MatrixFactorizationExample
     {
-        // The following variables defines the shape of a matrix. Its shape is _synthesizedMatrixRowCount-by-_synthesizedMatrixColumnCount.
-        // Because in ML.NET key type's minimal value is zero, the first row index is always zero in C# data structure (e.g., MatrixColumnIndex=0
-        // and MatrixRowIndex=0 in MatrixElement below specifies the value at the upper-left corner in the training matrix). If user's row index 
-        // starts with 1, their row index 1 would be mapped to the 2nd row in matrix factorization module and their first row may contain no values.
-        // This behavior is also true to column index.
-        const int _synthesizedMatrixFirstColumnIndex = 1;
-        const int _synthesizedMatrixFirstRowIndex = 1;
-        const int _synthesizedMatrixColumnCount = 60;
-        const int _synthesizedMatrixRowCount = 100;
-
-        // A data structure used to encode a single value in matrix
-        internal class MatrixElement
-        {
-            // Matrix column index is at most _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex.
-            [KeyType(Count = _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex)]
-            public uint MatrixColumnIndex;
-            // Matrix row index is at most _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex.
-            [KeyType(Count = _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex)]
-            public uint MatrixRowIndex;
-            // The value at the column MatrixColumnIndex and row MatrixRowIndex.
-            public float Value;
-        }
-
-        // A data structure used to encode prediction result. Comparing with MatrixElement, The field Value in MatrixElement is
-        // renamed to Score because Score is the default name of matrix factorization's output.
-        internal class MatrixElementForScore
-        {
-            [KeyType(Count = _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex)]
-            public uint MatrixColumnIndex;
-            [KeyType(Count = _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex)]
-            public uint MatrixRowIndex;
-            public float Score;
-        }
 
         // This example first creates in-memory data and then use it to train a matrix factorization model. Afterward, quality metrics are reported.
-        public static void MatrixFactorizationInMemoryData()
+        public static void MatrixFactorizationWithOptions()
         {
-            // Create an in-memory matrix as a list of tuples (column index, row index, value).
-            var dataMatrix = new List<MatrixElement>();
-            for (uint i = _synthesizedMatrixFirstColumnIndex; i < _synthesizedMatrixFirstColumnIndex + _synthesizedMatrixColumnCount; ++i)
-                for (uint j = _synthesizedMatrixFirstRowIndex; j < _synthesizedMatrixFirstRowIndex + _synthesizedMatrixRowCount; ++j)
-                    dataMatrix.Add(new MatrixElement() { MatrixColumnIndex = i, MatrixRowIndex = j, Value = (i + j) % 5 });
-
             // Create a new context for ML.NET operations. It can be used for exception tracking and logging,
             // as a catalog of available operations and as the source of randomness.
             var mlContext = new MLContext(seed: 0, conc: 1);
 
+            // Get a small in-memory dataset.
+            var data = GetRecommendationData();
+
             // Convert the in-memory matrix into an IDataView so that ML.NET components can consume it.
-            var dataView = mlContext.Data.ReadFromEnumerable(dataMatrix);
+            var dataView = mlContext.Data.ReadFromEnumerable(data);
 
             // Create a matrix factorization trainer which may consume "Value" as the training label, "MatrixColumnIndex" as the
             // matrix's column index, and "MatrixRowIndex" as the matrix's row index. Here nameof(...) is used to extract field
             // names' in MatrixElement class.
-
             var options = new MatrixFactorizationTrainer.Options
             {
                 MatrixColumnIndexColumnName = nameof(MatrixElement.MatrixColumnIndex),
@@ -68,7 +32,8 @@ public static void MatrixFactorizationInMemoryData()
                 LabelColumnName = nameof(MatrixElement.Value),
                 NumIterations = 10,
                 NumThreads = 1,
-                K = 32,
+                ApproximationRank = 32,
+                LearningRate = 0.3
             };
 
             var pipeline = mlContext.Recommendation().Trainers.MatrixFactorization(options);
@@ -84,11 +49,11 @@ public static void MatrixFactorizationInMemoryData()
                 label: nameof(MatrixElement.Value), score: nameof(MatrixElementForScore.Score));
 
             // Print out some metrics for checking the model's quality.
-            Console.WriteLine($"L1 - {metrics.L1}");
-            Console.WriteLine($"L2 - {metrics.L2}");
-            Console.WriteLine($"LossFunction - {metrics.LossFn}");
-            Console.WriteLine($"RMS - {metrics.Rms}");
-            Console.WriteLine($"RSquared - {metrics.RSquared}");
+            Console.WriteLine($"L1 - {metrics.L1}"); // 0.16375
+            Console.WriteLine($"L2 - {metrics.L2}"); // 0.04407
+            Console.WriteLine($"LossFunction - {metrics.LossFn}"); // 0.04407
+            Console.WriteLine($"RMS - {metrics.Rms}"); // 0.2099
+            Console.WriteLine($"RSquared - {metrics.RSquared}"); // 0.97797
 
             // Create two two entries for making prediction. Of course, the prediction value, Score, is unknown so it can be anything
             // (here we use Score=0 and it will be overwritten by the true prediction). If any of row and column indexes are out-of-range
@@ -101,8 +66,10 @@ public static void MatrixFactorizationInMemoryData()
             var testDataView = mlContext.Data.ReadFromEnumerable(testMatrix);
 
             // Feed the test data into the model and then iterate through all predictions.
-            foreach (var pred in mlContext.CreateEnumerable<MatrixElementForScore>(testDataView, false))
-                Console.WriteLine($"Predicted value at row {pred.MatrixRowIndex} and column {pred.MatrixColumnIndex} is {pred.Score}");
+            foreach (var pred in mlContext.CreateEnumerable<MatrixElementForScore>(model.Transform(testDataView), false))
+                Console.WriteLine($"Predicted value at row {pred.MatrixRowIndex-1} and column {pred.MatrixColumnIndex-1} is {pred.Score}");
+            // Predicted value at row 7 and column 1 is 2.828761
+            // Predicted value at row 6 and column 3 is 3.642226
         }
     }
 }
diff --git a/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs b/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
index 8642188c2f..901a535307 100644
--- a/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
+++ b/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
@@ -16,7 +16,7 @@
 using Microsoft.ML.Recommender.Internal;
 using Microsoft.ML.Trainers.Recommender;
 
-[assembly: LoadableClass(typeof(MatrixFactorizationPredictor), null, typeof(SignatureLoadModel), "Matrix Factorization Predictor Executor", MatrixFactorizationPredictor.LoaderSignature)]
+[assembly: LoadableClass(typeof(MatrixFactorizationModelParameters), null, typeof(SignatureLoadModel), "Matrix Factorization Predictor Executor", MatrixFactorizationModelParameters.LoaderSignature)]
 
 [assembly: LoadableClass(typeof(MatrixFactorizationPredictionTransformer), typeof(MatrixFactorizationPredictionTransformer),
     null, typeof(SignatureLoadModel), "", MatrixFactorizationPredictionTransformer.LoaderSignature)]
@@ -24,12 +24,15 @@
 namespace Microsoft.ML.Trainers.Recommender
 {
     /// <summary>
-    /// <see cref="MatrixFactorizationPredictor"/> stores two factor matrices, P and Q, for approximating the training matrix, R, by P * Q,
-    /// where * is a matrix multiplication. This predictor expects two inputs, row index and column index, and produces the (approximated)
+    /// Model parameters for matrix factorization recommender.
+    /// </summary>
+    /// <remarks>
+    /// <see cref="MatrixFactorizationModelParameters"/> stores two factor matrices, P and Q, for approximating the training matrix, R, by P * Q,
+    /// where * is a matrix multiplication. This model expects two inputs, row index and column index, and produces the (approximated)
     /// value at the location specified by the two inputs in R. More specifically, if input row and column indices are u and v, respectively.
     /// The output (a scalar) would be the inner product product of the u-th row in P and the v-th column in Q.
-    /// </summary>
-    public sealed class MatrixFactorizationPredictor : IPredictor, ICanSaveModel, ICanSaveInTextFormat, ISchemaBindableMapper
+    /// </remarks>
+    public sealed class MatrixFactorizationModelParameters : IPredictor, ICanSaveModel, ICanSaveInTextFormat, ISchemaBindableMapper
     {
         internal const string LoaderSignature = "MFPredictor";
         internal const string RegistrationName = "MatrixFactorizationPredictor";
@@ -43,33 +46,42 @@ private static VersionInfo GetVersionInfo()
                 verReadableCur: 0x00010001,
                 verWeCanReadBack: 0x00010001,
                 loaderSignature: LoaderSignature,
-                loaderAssemblyName: typeof(MatrixFactorizationPredictor).Assembly.FullName);
+                loaderAssemblyName: typeof(MatrixFactorizationModelParameters).Assembly.FullName);
         }
         private const uint VersionNoMinCount = 0x00010002;
 
         private readonly IHost _host;
-        // The number of rows.
-        private readonly int _numberOfRows;
-        // The number of columns.
-        private readonly int _numberofColumns;
-        // The rank of the factor matrices.
-        private readonly int _approximationRank;
-        // Packed _numberOfRows by _approximationRank matrix.
-        private readonly float[] _leftFactorMatrix;
-        // Packed _approximationRank by _numberofColumns matrix.
-        private readonly float[] _rightFactorMatrix;
-
-        public PredictionKind PredictionKind
-        {
-            get { return PredictionKind.Recommendation; }
-        }
+        ///<summary> The number of rows.</summary>
+        public readonly int NumberOfRows;
+        ///<summary> The number of columns.</summary>
+        public readonly int NumberOfColumns;
+        ///<summary> The rank of the factor matrices.</summary>
+        public readonly int ApproximationRank;
+        /// <summary>
+        /// Left approximation matrix
+        /// </summary>
+        /// <remarks>
+        /// This is two dimensional matrix with size of <see cref="NumberOfRows"/> * <see cref="ApproximationRank"/> flattened into one-dimensional matrix.
+        /// Row by row.
+        /// </remarks>
+        public readonly IReadOnlyList<float> LeftFactorMatrix;
+        /// <summary>
+        /// Left approximation matrix
+        /// </summary>
+        /// <remarks>
+        /// This is two dimensional matrix with size of <see cref="ApproximationRank"/> * <see cref="NumberOfColumns"/> flattened into one-dimensional matrix.
+        /// Row by row.
+        /// </remarks>
+        public readonly IReadOnlyList<float> RightFactorMatrix;
+
+        public PredictionKind PredictionKind => PredictionKind.Recommendation;
 
-        public ColumnType OutputType { get { return NumberType.Float; } }
+        private ColumnType OutputType => NumberType.Float;
 
-        public ColumnType MatrixColumnIndexType { get; }
-        public ColumnType MatrixRowIndexType { get; }
+        internal ColumnType MatrixColumnIndexType { get; }
+        internal ColumnType MatrixRowIndexType { get; }
 
-        internal MatrixFactorizationPredictor(IHostEnvironment env, SafeTrainingAndModelBuffer buffer, KeyType matrixColumnIndexType, KeyType matrixRowIndexType)
+        internal MatrixFactorizationModelParameters(IHostEnvironment env, SafeTrainingAndModelBuffer buffer, KeyType matrixColumnIndexType, KeyType matrixRowIndexType)
         {
             Contracts.CheckValue(env, nameof(env));
             _host = env.Register(RegistrationName);
@@ -78,18 +90,19 @@ internal MatrixFactorizationPredictor(IHostEnvironment env, SafeTrainingAndModel
             _host.CheckValue(buffer, nameof(buffer));
             _host.CheckValue(matrixColumnIndexType, nameof(matrixColumnIndexType));
             _host.CheckValue(matrixRowIndexType, nameof(matrixRowIndexType));
-
-            buffer.Get(out _numberOfRows, out _numberofColumns, out _approximationRank, out _leftFactorMatrix, out _rightFactorMatrix);
-            _host.Assert(_numberofColumns == matrixColumnIndexType.GetCountAsInt32(_host));
-            _host.Assert(_numberOfRows == matrixRowIndexType.GetCountAsInt32(_host));
-            _host.Assert(_leftFactorMatrix.Length == _numberOfRows * _approximationRank);
-            _host.Assert(_rightFactorMatrix.Length == _numberofColumns * _approximationRank);
+            buffer.Get(out NumberOfRows, out NumberOfColumns, out ApproximationRank, out var leftFactorMatrix, out var rightFactorMatrix);
+            LeftFactorMatrix = leftFactorMatrix;
+            RightFactorMatrix = rightFactorMatrix;
+            _host.Assert(NumberOfColumns == matrixColumnIndexType.GetCountAsInt32(_host));
+            _host.Assert(NumberOfRows == matrixRowIndexType.GetCountAsInt32(_host));
+            _host.Assert(LeftFactorMatrix.Count == NumberOfRows * ApproximationRank);
+            _host.Assert(RightFactorMatrix.Count == ApproximationRank * NumberOfColumns);
 
             MatrixColumnIndexType = matrixColumnIndexType;
             MatrixRowIndexType = matrixRowIndexType;
         }
 
-        private MatrixFactorizationPredictor(IHostEnvironment env, ModelLoadContext ctx)
+        private MatrixFactorizationModelParameters(IHostEnvironment env, ModelLoadContext ctx)
         {
             Contracts.CheckValue(env, nameof(env));
             _host = env.Register(RegistrationName);
@@ -100,43 +113,43 @@ private MatrixFactorizationPredictor(IHostEnvironment env, ModelLoadContext ctx)
             // float[m * k]: the left factor matrix
             // float[k * n]: the right factor matrix
 
-            _numberOfRows = ctx.Reader.ReadInt32();
-            _host.CheckDecode(_numberOfRows > 0);
+            NumberOfRows = ctx.Reader.ReadInt32();
+            _host.CheckDecode(NumberOfRows > 0);
             if (ctx.Header.ModelVerWritten < VersionNoMinCount)
             {
                 ulong mMin = ctx.Reader.ReadUInt64();
                 // We no longer support non zero Min for KeyType.
                 _host.CheckDecode(mMin == 0);
-                _host.CheckDecode((ulong)_numberOfRows <= ulong.MaxValue - mMin);
+                _host.CheckDecode((ulong)NumberOfRows <= ulong.MaxValue - mMin);
             }
-            _numberofColumns = ctx.Reader.ReadInt32();
-            _host.CheckDecode(_numberofColumns > 0);
+            NumberOfColumns = ctx.Reader.ReadInt32();
+            _host.CheckDecode(NumberOfColumns > 0);
             if (ctx.Header.ModelVerWritten < VersionNoMinCount)
             {
                 ulong nMin = ctx.Reader.ReadUInt64();
                 // We no longer support non zero Min for KeyType.
                 _host.CheckDecode(nMin == 0);
-                _host.CheckDecode((ulong)_numberofColumns <= ulong.MaxValue - nMin);
+                _host.CheckDecode((ulong)NumberOfColumns <= ulong.MaxValue - nMin);
             }
-            _approximationRank = ctx.Reader.ReadInt32();
-            _host.CheckDecode(_approximationRank > 0);
+            ApproximationRank = ctx.Reader.ReadInt32();
+            _host.CheckDecode(ApproximationRank > 0);
 
-            _leftFactorMatrix = Utils.ReadSingleArray(ctx.Reader, checked(_numberOfRows * _approximationRank));
-            _rightFactorMatrix = Utils.ReadSingleArray(ctx.Reader, checked(_numberofColumns * _approximationRank));
+            LeftFactorMatrix = Utils.ReadSingleArray(ctx.Reader, checked(NumberOfRows * ApproximationRank));
+            RightFactorMatrix = Utils.ReadSingleArray(ctx.Reader, checked(NumberOfColumns * ApproximationRank));
 
-            MatrixColumnIndexType = new KeyType(typeof(uint), _numberofColumns);
-            MatrixRowIndexType = new KeyType(typeof(uint), _numberOfRows);
+            MatrixColumnIndexType = new KeyType(typeof(uint), NumberOfColumns);
+            MatrixRowIndexType = new KeyType(typeof(uint), NumberOfRows);
         }
 
         /// <summary>
         /// Load model from the given context
         /// </summary>
-        public static MatrixFactorizationPredictor Create(IHostEnvironment env, ModelLoadContext ctx)
+        private static MatrixFactorizationModelParameters Create(IHostEnvironment env, ModelLoadContext ctx)
         {
             Contracts.CheckValue(env, nameof(env));
             env.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel(GetVersionInfo());
-            return new MatrixFactorizationPredictor(env, ctx);
+            return new MatrixFactorizationModelParameters(env, ctx);
         }
 
         /// <summary>
@@ -154,16 +167,16 @@ public void Save(ModelSaveContext ctx)
             // float[m * k]: the left factor matrix
             // float[k * n]: the right factor matrix
 
-            _host.Check(_numberOfRows > 0, "Number of rows must be positive");
-            _host.Check(_numberofColumns > 0, "Number of columns must be positive");
-            _host.Check(_approximationRank > 0, "Number of latent factors must be positive");
-            ctx.Writer.Write(_numberOfRows);
-            ctx.Writer.Write(_numberofColumns);
-            ctx.Writer.Write(_approximationRank);
-            _host.Check(Utils.Size(_leftFactorMatrix) == _numberOfRows * _approximationRank, "Unexpected matrix size of a factor matrix (matrix P in LIBMF paper)");
-            _host.Check(Utils.Size(_rightFactorMatrix) == _numberofColumns * _approximationRank, "Unexpected matrix size of a factor matrix (matrix Q in LIBMF paper)");
-            Utils.WriteSinglesNoCount(ctx.Writer, _leftFactorMatrix.AsSpan(0, _numberOfRows * _approximationRank));
-            Utils.WriteSinglesNoCount(ctx.Writer, _rightFactorMatrix.AsSpan(0, _numberofColumns * _approximationRank));
+            _host.Check(NumberOfRows > 0, "Number of rows must be positive");
+            _host.Check(NumberOfColumns > 0, "Number of columns must be positive");
+            _host.Check(ApproximationRank > 0, "Number of latent factors must be positive");
+            ctx.Writer.Write(NumberOfRows);
+            ctx.Writer.Write(NumberOfColumns);
+            ctx.Writer.Write(ApproximationRank);
+            _host.Check(Utils.Size(LeftFactorMatrix) == NumberOfRows * ApproximationRank, "Unexpected matrix size of a factor matrix (matrix P in LIBMF paper)");
+            _host.Check(Utils.Size(RightFactorMatrix) == NumberOfColumns * ApproximationRank, "Unexpected matrix size of a factor matrix (matrix Q in LIBMF paper)");
+            Utils.WriteSinglesNoCount(ctx.Writer, LeftFactorMatrix as float[]);
+            Utils.WriteSinglesNoCount(ctx.Writer, RightFactorMatrix as float[]);
         }
 
         /// <summary>
@@ -172,20 +185,20 @@ public void Save(ModelSaveContext ctx)
         void ICanSaveInTextFormat.SaveAsText(TextWriter writer, RoleMappedSchema schema)
         {
             writer.WriteLine("# Imputed matrix is P * Q'");
-            writer.WriteLine("# P in R^({0} x {1}), rows correpond to Y item", _numberOfRows, _approximationRank);
-            for (int i = 0; i < _leftFactorMatrix.Length; ++i)
+            writer.WriteLine("# P in R^({0} x {1}), rows correpond to Y item", NumberOfRows, ApproximationRank);
+            for (int i = 0; i < LeftFactorMatrix.Count; ++i)
             {
-                writer.Write(_leftFactorMatrix[i].ToString("G"));
-                if (i % _approximationRank == _approximationRank - 1)
+                writer.Write(LeftFactorMatrix[i].ToString("G"));
+                if (i % ApproximationRank == ApproximationRank - 1)
                     writer.WriteLine();
                 else
                     writer.Write('\t');
             }
-            writer.WriteLine("# Q in R^({0} x {1}), rows correpond to X item", _numberofColumns, _approximationRank);
-            for (int i = 0; i < _rightFactorMatrix.Length; ++i)
+            writer.WriteLine("# Q in R^({0} x {1}), rows correpond to X item", NumberOfColumns, ApproximationRank);
+            for (int i = 0; i < RightFactorMatrix.Count; ++i)
             {
-                writer.Write(_rightFactorMatrix[i].ToString("G"));
-                if (i % _approximationRank == _approximationRank - 1)
+                writer.Write(RightFactorMatrix[i].ToString("G"));
+                if (i % ApproximationRank == ApproximationRank - 1)
                     writer.WriteLine();
                 else
                     writer.Write('\t');
@@ -241,7 +254,7 @@ private void MapperCore(in uint srcCol, ref uint srcRow, ref float dst)
             // training. For higher-than-expected values, the predictor version would return
             // 0, rather than NaN as we do here. It is in my mind an open question as to what
             // is actually correct.
-            if (srcRow == 0 || srcRow > _numberOfRows || srcCol == 0 || srcCol > _numberofColumns)
+            if (srcRow == 0 || srcRow > NumberOfRows || srcCol == 0 || srcCol > NumberOfColumns)
             {
                 dst = float.NaN;
                 return;
@@ -251,15 +264,15 @@ private void MapperCore(in uint srcCol, ref uint srcRow, ref float dst)
 
         private float Score(int columnIndex, int rowIndex)
         {
-            _host.Assert(0 <= rowIndex && rowIndex < _numberOfRows);
-            _host.Assert(0 <= columnIndex && columnIndex < _numberofColumns);
+            _host.Assert(0 <= rowIndex && rowIndex < NumberOfRows);
+            _host.Assert(0 <= columnIndex && columnIndex < NumberOfColumns);
             float score = 0;
             // Starting position of the rowIndex-th row in the left factor factor matrix
-            int rowOffset = rowIndex * _approximationRank;
+            int rowOffset = rowIndex * ApproximationRank;
             // Starting position of the columnIndex-th column in the right factor factor matrix
-            int columnOffset = columnIndex * _approximationRank;
-            for (int i = 0; i < _approximationRank; i++)
-                score += _leftFactorMatrix[rowOffset + i] * _rightFactorMatrix[columnOffset + i];
+            int columnOffset = columnIndex * ApproximationRank;
+            for (int i = 0; i < ApproximationRank; i++)
+                score += LeftFactorMatrix[rowOffset + i] * RightFactorMatrix[columnOffset + i];
             return score;
         }
 
@@ -276,7 +289,7 @@ ISchemaBoundMapper ISchemaBindableMapper.Bind(IHostEnvironment env, RoleMappedSc
 
         private sealed class RowMapper : ISchemaBoundRowMapper
         {
-            private readonly MatrixFactorizationPredictor _parent;
+            private readonly MatrixFactorizationModelParameters _parent;
             // The tail "ColumnIndex" means the column index in IDataView
             private readonly int _matrixColumnIndexColumnIndex;
             private readonly int _matrixRowIndexCololumnIndex;
@@ -289,7 +302,7 @@ private sealed class RowMapper : ISchemaBoundRowMapper
 
             public RoleMappedSchema InputRoleMappedSchema { get; }
 
-            public RowMapper(IHostEnvironment env, MatrixFactorizationPredictor parent, RoleMappedSchema schema, Schema outputSchema)
+            public RowMapper(IHostEnvironment env, MatrixFactorizationModelParameters parent, RoleMappedSchema schema, Schema outputSchema)
             {
                 Contracts.AssertValue(parent);
                 _env = env;
@@ -375,13 +388,16 @@ public Row GetRow(Row input, Func<int, bool> active)
         }
     }
 
-    public sealed class MatrixFactorizationPredictionTransformer : PredictionTransformerBase<MatrixFactorizationPredictor>, ICanSaveModel
+    /// <summary>
+    /// Trains a <see cref="MatrixFactorizationModelParameters"/>. It factorizes the training matrix into the product of two low-rank matrices.
+    /// </summary>
+    public sealed class MatrixFactorizationPredictionTransformer : PredictionTransformerBase<MatrixFactorizationModelParameters>, ICanSaveModel
     {
-        public const string LoaderSignature = "MaFactPredXf";
-        public string MatrixColumnIndexColumnName { get; }
-        public string MatrixRowIndexColumnName { get; }
-        public ColumnType MatrixColumnIndexColumnType { get; }
-        public ColumnType MatrixRowIndexColumnType { get; }
+        internal const string LoaderSignature = "MaFactPredXf";
+        internal string MatrixColumnIndexColumnName { get; }
+        internal string MatrixRowIndexColumnName { get; }
+        internal ColumnType MatrixColumnIndexColumnType { get; }
+        internal ColumnType MatrixRowIndexColumnType { get; }
 
         /// <summary>
         /// Build a transformer based on matrix factorization predictor (model) and the input schema (trainSchema). The created
@@ -395,7 +411,7 @@ public sealed class MatrixFactorizationPredictionTransformer : PredictionTransfo
         /// <param name="matrixColumnIndexColumnName">The name of the column used as role <see cref="RecommenderUtils.MatrixColumnIndexKind"/> in matrix factorization world</param>
         /// <param name="matrixRowIndexColumnName">The name of the column used as role <see cref="RecommenderUtils.MatrixRowIndexKind"/> in matrix factorization world</param>
         /// <param name="scoreColumnNameSuffix">A string attached to the output column name of this transformer</param>
-        public MatrixFactorizationPredictionTransformer(IHostEnvironment env, MatrixFactorizationPredictor model, Schema trainSchema,
+        internal MatrixFactorizationPredictionTransformer(IHostEnvironment env, MatrixFactorizationModelParameters model, Schema trainSchema,
             string matrixColumnIndexColumnName, string matrixRowIndexColumnName, string scoreColumnNameSuffix = "")
             : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(MatrixFactorizationPredictionTransformer)), model, trainSchema)
         {
@@ -432,7 +448,7 @@ private RoleMappedSchema GetSchema()
         /// The counter constructor of re-creating <see cref="MatrixFactorizationPredictionTransformer"/> from the context where
         /// the original transform is saved.
         /// </summary>
-        public MatrixFactorizationPredictionTransformer(IHostEnvironment host, ModelLoadContext ctx)
+        private MatrixFactorizationPredictionTransformer(IHostEnvironment host, ModelLoadContext ctx)
             : base(Contracts.CheckRef(host, nameof(host)).Register(nameof(MatrixFactorizationPredictionTransformer)), ctx)
         {
             // *** Binary format ***
@@ -458,6 +474,10 @@ public MatrixFactorizationPredictionTransformer(IHostEnvironment host, ModelLoad
             Scorer = new GenericScorer(Host, args, new EmptyDataView(Host, TrainSchema), BindableMapper.Bind(Host, schema), schema);
         }
 
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
         public override Schema GetOutputSchema(Schema inputSchema)
         {
             if (!inputSchema.TryGetColumnIndex(MatrixColumnIndexColumnName, out int xCol))
diff --git a/src/Microsoft.ML.Recommender/MatrixFactorizationTrainer.cs b/src/Microsoft.ML.Recommender/MatrixFactorizationTrainer.cs
index c3696d22bf..e09eab6ace 100644
--- a/src/Microsoft.ML.Recommender/MatrixFactorizationTrainer.cs
+++ b/src/Microsoft.ML.Recommender/MatrixFactorizationTrainer.cs
@@ -83,15 +83,37 @@ namespace Microsoft.ML.Trainers
     /// <example>
     /// <format type="text/markdown">
     /// <![CDATA[
-    /// [!code-csharp[MF](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/MatrixFactorization.cs)]
+    /// [!code-csharp[MF](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs)]
     /// ]]>
     /// </format>
     /// </example>
-    public sealed class MatrixFactorizationTrainer : TrainerBase<MatrixFactorizationPredictor>,
+    public sealed class MatrixFactorizationTrainer : TrainerBase<MatrixFactorizationModelParameters>,
         IEstimator<MatrixFactorizationPredictionTransformer>
     {
-        public enum LossFunctionType { SquareLossRegression = 0, SquareLossOneClass = 12 };
+        /// <summary>
+        /// Type of loss function.
+        /// </summary>
+        public enum LossFunctionType
+        {
+            /// <summary>
+            /// Used in traditional collaborative filtering problem with squared loss.
+            /// </summary>
+            /// <remarks>
+            /// See <a href="https://www.csie.ntu.edu.tw/~cjlin/papers/libmf/mf_adaptive_pakdd.pdf">Equation</a> (1).
+            /// </remarks>
+            SquareLossRegression = 0,
+            /// <summary>
+            /// Used in implicit-feedback recommendation problem.
+            /// </summary>
+            /// <remarks>
+            /// See <a href="http://yifanhu.net/PUB/cf.pdf">Equation</a> (3).
+            /// </remarks>
+            SquareLossOneClass = 12
+        };
 
+        /// <summary>
+        /// Advanced options for the <see cref="MatrixFactorizationTrainer"/>.
+        /// </summary>
         public sealed class Options
         {
             /// <summary>
@@ -110,40 +132,69 @@ public sealed class Options
             public string LabelColumnName;
 
             /// <summary>
-            /// Loss function minimized for finding factor matrices.  Two values are allowed, 0 or 12. The values 0 means traditional collaborative filtering
-            /// problem with squared loss. The value 12 triggers one-class matrix factorization for implicit-feedback recommendation problem.
+            /// Loss function minimized for finding factor matrices.
             /// </summary>
+            /// <remarks>
+            /// Two values are allowed, <see cref="LossFunctionType.SquareLossRegression"/> or <see cref="LossFunctionType.SquareLossOneClass"/>.
+            /// The <see cref="LossFunctionType.SquareLossRegression"/> means traditional collaborative filtering problem with squared loss.
+            /// The <see cref="LossFunctionType.SquareLossOneClass"/> triggers one-class matrix factorization for implicit-feedback recommendation problem.
+            /// </remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Loss function minimized for finding factor matrices.")]
             [TGUI(SuggestedSweeps = "0,12")]
             [TlcModule.SweepableDiscreteParam("LossFunction", new object[] { LossFunctionType.SquareLossRegression, LossFunctionType.SquareLossOneClass })]
-            public LossFunctionType LossFunction = LossFunctionType.SquareLossRegression;
+            public LossFunctionType LossFunction = Defaults.LossFunction;
 
+            /// <summary>
+            /// Regularization parameter.
+            /// </summary>
+            /// <remarks>
+            /// It's the weight of factor matrices Frobenius norms in the objective function minimized by matrix factorization's algorithm. A small value could cause over-fitting.
+            /// </remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Regularization parameter. " +
-                "It's the weight of factor matrices' norms in the objective function minimized by matrix factorization's algorithm. " +
+                "It's the weight of factor matrices Frobenius norms in the objective function minimized by matrix factorization's algorithm. " +
                 "A small value could cause over-fitting.")]
             [TGUI(SuggestedSweeps = "0.01,0.05,0.1,0.5,1")]
             [TlcModule.SweepableDiscreteParam("Lambda", new object[] { 0.01f, 0.05f, 0.1f, 0.5f, 1f })]
-            public double Lambda = 0.1;
+            public double Lambda = Defaults.Lambda;
 
+            /// <summary>
+            /// Rank of approximation matrixes.
+            /// </summary>
+            /// <remarks>
+            /// If input data has size of m-by-n we would build two approximation matrixes m-by-k and k-by-n where k is approximation rank.
+            /// </remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Latent space dimension (denoted by k). If the factorized matrix is m-by-n, " +
                 "two factor matrices found by matrix factorization are m-by-k and k-by-n, respectively. " +
-                "This value is also known as the rank of matrix factorization because k is generally much smaller than m and n.")]
+                "This value is also known as the rank of matrix factorization because k is generally much smaller than m and n.", ShortName = "K")]
             [TGUI(SuggestedSweeps = "8,16,64,128")]
             [TlcModule.SweepableDiscreteParam("K", new object[] { 8, 16, 64, 128 })]
-            public int K = 8;
+            public int ApproximationRank = Defaults.ApproximationRank;
 
+            /// <summary>
+            /// Number of training iterations.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Training iterations; that is, the times that the training algorithm iterates through the whole training data once.", ShortName = "iter")]
             [TGUI(SuggestedSweeps = "10,20,40")]
             [TlcModule.SweepableDiscreteParam("NumIterations", new object[] { 10, 20, 40 })]
-            public int NumIterations = 20;
-
+            public int NumIterations = Defaults.NumIterations;
+
+            ///<summary>
+            /// Initial learning rate. It specifies the speed of the training algorithm.
+            ///</summary>
+            ///<remarks>
+            /// Small value may increase the number of iterations needed to achieve a reasonable result.
+            /// Large value may lead to numerical difficulty such as a infinity value.
+            ///</remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Initial learning rate. It specifies the speed of the training algorithm. " +
-                "Small value may increase the number of iterations needed to achieve a reasonable result. Large value may lead to numerical difficulty such as a infinity value.")]
+                "Small value may increase the number of iterations needed to achieve a reasonable result. Large value may lead to numerical difficulty such as a infinity value.", ShortName = "Eta")]
             [TGUI(SuggestedSweeps = "0.001,0.01,0.1")]
             [TlcModule.SweepableDiscreteParam("Eta", new object[] { 0.001f, 0.01f, 0.1f })]
-            public double Eta = 0.1;
+            public double LearningRate = Defaults.LearningRate;
 
             /// <summary>
+            /// Importance of unobserved entries' loss in one-class matrix factorization. Applicable if <see cref="LossFunction"/> set to <see cref="LossFunctionType.SquareLossOneClass"/>
+            /// </summary>
+            /// <remarks>
             /// Importance of unobserved (i.e., negative) entries' loss in one-class matrix factorization.
             /// In general, only a few of matrix entries (e.g., less than 1%) in the training are observed (i.e., positive).
             /// To balance the contributions from unobserved and obverved in the overall loss function, this parameter is
@@ -154,32 +205,57 @@ public sealed class Options
             /// Alpha = (# of observed entries) / (# of unobserved entries) can make observed and unobserved entries equally important
             /// in the minimized loss function. However, the best setting in machine learning is alwasy data-depedent so user still needs to
             /// try multiple values.
-            /// </summary>
+            /// </remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Importance of unobserved entries' loss in one-class matrix factorization.")]
             [TGUI(SuggestedSweeps = "1,0.01,0.0001,0.000001")]
             [TlcModule.SweepableDiscreteParam("Alpha", new object[] { 1f, 0.01f, 0.0001f, 0.000001f })]
-            public double Alpha = 0.0001;
+            public double Alpha = Defaults.Alpha;
 
             /// <summary>
-            /// Desired negative entries value in one-class matrix factorization. In one-class matrix factorization, all matrix values observed are one
-            /// (which can be viewed as positive cases in binary classification) while unobserved values (which can be viewed as negative cases in binary
-            /// classification) need to be specified manually using this option.
+            /// Desired negative entries value in one-class matrix factorization. Applicable if <see cref="LossFunction"/> set to <see cref="LossFunctionType.SquareLossOneClass"/>
             /// </summary>
+            /// <remarks>
+            /// In one-class matrix factorization, all matrix values observed are one (which can be viewed as positive cases in binary classification)
+            /// while unobserved values (which can be viewed as negative cases in binary classification) need to be specified manually using this option.
+            /// </remarks>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Desired negative entries' value in one-class matrix factorization")]
             [TGUI(SuggestedSweeps = "0.000001,0,0001,0.01")]
             [TlcModule.SweepableDiscreteParam("C", new object[] { 0.000001f, 0.0001f, 0.01f })]
-            public double C = 0.000001f;
+            public double C = Defaults.C;
 
+            /// <summary>
+            /// Number of threads will be used during training. If unspecified all aviable threads will be use.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Number of threads can be used in the training procedure.", ShortName = "t")]
             public int? NumThreads;
 
+            /// <summary>
+            /// Suppress writing additional information to output.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Suppress writing additional information to output.")]
-            public bool Quiet;
+            public bool Quiet = Defaults.Quiet;
 
+            /// <summary>
+            /// Force the factor matrices to be non-negative.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "Force the factor matrices to be non-negative.", ShortName = "nn")]
-            public bool NonNegative;
+            public bool NonNegative = Defaults.NonNegative;
         };
 
+        [BestFriend]
+        internal static class Defaults
+        {
+            public const bool Quiet = false;
+            public const bool NonNegative = false;
+            public const double C = 0.000001f;
+            public const double Alpha = 0.0001f;
+            public const double LearningRate = 0.1;
+            public const int NumIterations = 20;
+            public const int ApproximationRank = 8;
+            public const double Lambda = 0.1;
+            public const LossFunctionType LossFunction = LossFunctionType.SquareLossRegression;
+        }
+
         internal const string Summary = "From pairs of row/column indices and a value of a matrix, this trains a predictor capable of filling in unknown entries of the matrix, "
             + "using a low-rank matrix factorization. This technique is often used in recommender system, where the row and column indices indicate users and items, "
             + "and the values of the matrix are ratings. ";
@@ -197,7 +273,8 @@ public sealed class Options
         private readonly bool _doNmf;
 
         public override PredictionKind PredictionKind => PredictionKind.Recommendation;
-        public const string LoadNameValue = "MatrixFactorization";
+
+        internal const string LoadNameValue = "MatrixFactorization";
 
         /// <summary>
         /// The row index, column index, and label columns needed to specify the training matrix. This trainer uses tuples of (row index, column index, label value) to specify a matrix.
@@ -238,18 +315,18 @@ internal MatrixFactorizationTrainer(IHostEnvironment env, Options options) : bas
         {
             const string posError = "Parameter must be positive";
             Host.CheckValue(options, nameof(options));
-            Host.CheckUserArg(options.K > 0, nameof(options.K), posError);
+            Host.CheckUserArg(options.ApproximationRank > 0, nameof(options.ApproximationRank), posError);
             Host.CheckUserArg(!options.NumThreads.HasValue || options.NumThreads > 0, nameof(options.NumThreads), posError);
             Host.CheckUserArg(options.NumIterations > 0, nameof(options.NumIterations), posError);
             Host.CheckUserArg(options.Lambda > 0, nameof(options.Lambda), posError);
-            Host.CheckUserArg(options.Eta > 0, nameof(options.Eta), posError);
+            Host.CheckUserArg(options.LearningRate > 0, nameof(options.LearningRate), posError);
             Host.CheckUserArg(options.Alpha > 0, nameof(options.Alpha), posError);
 
             _fun = (int)options.LossFunction;
             _lambda = options.Lambda;
-            _k = options.K;
+            _k = options.ApproximationRank;
             _iter = options.NumIterations;
-            _eta = options.Eta;
+            _eta = options.LearningRate;
             _alpha = options.Alpha;
             _c = options.C;
             _threads = options.NumThreads ?? Environment.ProcessorCount;
@@ -270,21 +347,27 @@ internal MatrixFactorizationTrainer(IHostEnvironment env, Options options) : bas
         /// <param name="matrixColumnIndexColumnName">The name of the column hosting the matrix's column IDs.</param>
         /// <param name="matrixRowIndexColumnName">The name of the column hosting the matrix's row IDs.</param>
         /// <param name="labelColumn">The name of the label column.</param>
+        /// <param name="approximationRank">Rank of approximation matrixes.</param>
+        /// <param name="learningRate">Initial learning rate. It specifies the speed of the training algorithm.</param>
+        /// <param name="numIterations">Number of training iterations.</param>
         [BestFriend]
         internal MatrixFactorizationTrainer(IHostEnvironment env,
             string matrixColumnIndexColumnName,
             string matrixRowIndexColumnName,
-            string labelColumn = DefaultColumnNames.Label)
+            string labelColumn = DefaultColumnNames.Label,
+            int approximationRank = Defaults.ApproximationRank,
+            double learningRate = Defaults.LearningRate,
+            int numIterations = Defaults.NumIterations)
             : base(env, LoadNameValue)
         {
             var args = new Options();
 
             _fun = (int)args.LossFunction;
-            _lambda = args.Lambda;
-            _k = args.K;
-            _iter = args.NumIterations;
-            _eta = args.Eta;
+            _k = approximationRank;
+            _iter = numIterations;
+            _eta = learningRate;
             _alpha = args.Alpha;
+            _lambda = args.Lambda;
             _c = args.C;
             _threads = args.NumThreads ?? Environment.ProcessorCount;
             _quiet = args.Quiet;
@@ -301,7 +384,7 @@ internal MatrixFactorizationTrainer(IHostEnvironment env,
         /// Train a matrix factorization model based on training data, validation data, and so on in the given context.
         /// </summary>
         /// <param name="context">The information collection needed for training. <see cref="TrainContext"/> for details.</param>
-        private protected override MatrixFactorizationPredictor Train(TrainContext context)
+        private protected override MatrixFactorizationModelParameters Train(TrainContext context)
         {
             Host.CheckValue(context, nameof(context));
             using (var ch = Host.Start("Training"))
@@ -310,7 +393,7 @@ private protected override MatrixFactorizationPredictor Train(TrainContext conte
             }
         }
 
-        private MatrixFactorizationPredictor TrainCore(IChannel ch, RoleMappedData data, RoleMappedData validData = null)
+        private MatrixFactorizationModelParameters TrainCore(IChannel ch, RoleMappedData data, RoleMappedData validData = null)
         {
             Host.AssertValue(ch);
             ch.AssertValue(data);
@@ -321,7 +404,7 @@ private MatrixFactorizationPredictor TrainCore(IChannel ch, RoleMappedData data,
             var labelCol = data.Schema.Label.Value;
             if (labelCol.Type != NumberType.R4 && labelCol.Type != NumberType.R8)
                 throw ch.Except("Column '{0}' for label should be floating point, but is instead {1}", labelCol.Name, labelCol.Type);
-            MatrixFactorizationPredictor predictor;
+            MatrixFactorizationModelParameters predictor;
             if (validData != null)
             {
                 ch.CheckValue(validData, nameof(validData));
@@ -362,7 +445,7 @@ private MatrixFactorizationPredictor TrainCore(IChannel ch, RoleMappedData data,
                     using (var buffer = PrepareBuffer())
                     {
                         buffer.Train(ch, rowCount, colCount, cursor, labGetter, matrixRowIndexGetter, matrixColumnIndexGetter);
-                        predictor = new MatrixFactorizationPredictor(Host, buffer, (KeyType)matrixColumnIndexColInfo.Type, (KeyType)matrixRowIndexColInfo.Type);
+                        predictor = new MatrixFactorizationModelParameters(Host, buffer, (KeyType)matrixColumnIndexColInfo.Type, (KeyType)matrixRowIndexColInfo.Type);
                     }
                 }
                 else
@@ -380,7 +463,7 @@ private MatrixFactorizationPredictor TrainCore(IChannel ch, RoleMappedData data,
                             buffer.TrainWithValidation(ch, rowCount, colCount,
                                 cursor, labGetter, matrixRowIndexGetter, matrixColumnIndexGetter,
                                 validCursor, validLabelGetter, validMatrixRowIndexGetter, validMatrixColumnIndexGetter);
-                            predictor = new MatrixFactorizationPredictor(Host, buffer, (KeyType)matrixColumnIndexColInfo.Type, (KeyType)matrixRowIndexColInfo.Type);
+                            predictor = new MatrixFactorizationModelParameters(Host, buffer, (KeyType)matrixColumnIndexColInfo.Type, (KeyType)matrixRowIndexColInfo.Type);
                         }
                     }
                 }
@@ -403,7 +486,7 @@ private SafeTrainingAndModelBuffer PrepareBuffer()
         /// <param name="validationData">The validation data set.</param>
         public MatrixFactorizationPredictionTransformer Train(IDataView trainData, IDataView validationData = null)
         {
-            MatrixFactorizationPredictor model = null;
+            MatrixFactorizationModelParameters model = null;
 
             var roles = new List<KeyValuePair<RoleMappedSchema.ColumnRole, string>>();
             roles.Add(new KeyValuePair<RoleMappedSchema.ColumnRole, string>(RoleMappedSchema.ColumnRole.Label, LabelName));
@@ -427,6 +510,10 @@ public MatrixFactorizationPredictionTransformer Train(IDataView trainData, IData
         /// <param name="input">The training data set.</param>
         public MatrixFactorizationPredictionTransformer Fit(IDataView input) => Train(input);
 
+        /// <summary>
+        /// Schema propagation for transformers. Returns the output schema of the data, if
+        /// the input schema is like the one provided.
+        /// </summary>
         public SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
             Host.CheckValue(inputSchema, nameof(inputSchema));
diff --git a/src/Microsoft.ML.Recommender/RecommenderCatalog.cs b/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
index 448595ec41..4c98db5a74 100644
--- a/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
+++ b/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
@@ -55,11 +55,24 @@ internal RecommendationTrainers(RecommendationCatalog catalog)
             /// <param name="matrixColumnIndexColumnName">The name of the column hosting the matrix's column IDs.</param>
             /// <param name="matrixRowIndexColumnName">The name of the column hosting the matrix's row IDs.</param>
             /// <param name="labelColumn">The name of the label column.</param>
+            /// <param name="approximationRank">Rank of approximation matrixes.</param>
+            /// <param name="learningRate">Initial learning rate. It specifies the speed of the training algorithm.</param>
+            /// <param name="numIterations">Number of training iterations.</param>
+            /// <example>
+            /// <format type="text/markdown">
+            /// <![CDATA[
+            ///  [!code-csharp[MatrixFactorization](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorization.cs)]
+            /// ]]></format>
+            /// </example>
             public MatrixFactorizationTrainer MatrixFactorization(
                 string matrixColumnIndexColumnName,
                 string matrixRowIndexColumnName,
-                string labelColumn = DefaultColumnNames.Label)
-                    => new MatrixFactorizationTrainer(Owner.Environment, matrixColumnIndexColumnName, matrixRowIndexColumnName, labelColumn);
+                string labelColumn = DefaultColumnNames.Label,
+                int approximationRank = MatrixFactorizationTrainer.Defaults.ApproximationRank,
+                double learningRate = MatrixFactorizationTrainer.Defaults.LearningRate,
+                int numIterations = MatrixFactorizationTrainer.Defaults.NumIterations)
+                    => new MatrixFactorizationTrainer(Owner.Environment, matrixColumnIndexColumnName, matrixRowIndexColumnName, labelColumn,
+                        approximationRank, learningRate, numIterations);
 
             /// <summary>
             /// Train a matrix factorization model. It factorizes the training matrix into the product of two low-rank matrices.
@@ -74,7 +87,7 @@ public MatrixFactorizationTrainer MatrixFactorization(
             /// <example>
             /// <format type="text/markdown">
             /// <![CDATA[
-            ///  [!code-csharp[MatrixFactorization](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/MatrixFactorization.cs)]
+            ///  [!code-csharp[MatrixFactorization](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Recommendation/MatrixFactorizationWithOptions.cs)]
             /// ]]></format>
             /// </example>
             public MatrixFactorizationTrainer MatrixFactorization(
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index edaf2d55c5..000bf48f95 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -121,14 +121,14 @@ public static string DownloadTensorFlowSentimentModel()
             string remotePath = "https://github.com/dotnet/machinelearning-testdata/raw/master/Microsoft.ML.TensorFlow.TestModels/sentiment_model/";
 
             string path = "sentiment_model";
-            if(!Directory.Exists(path))
+            if (!Directory.Exists(path))
                 Directory.CreateDirectory(path);
 
             string varPath = Path.Combine(path, "variables");
             if (!Directory.Exists(varPath))
                 Directory.CreateDirectory(varPath);
 
-            Download(Path.Combine(remotePath, "saved_model.pb"), Path.Combine(path,"saved_model.pb"));
+            Download(Path.Combine(remotePath, "saved_model.pb"), Path.Combine(path, "saved_model.pb"));
             Download(Path.Combine(remotePath, "imdb_word_index.csv"), Path.Combine(path, "imdb_word_index.csv"));
             Download(Path.Combine(remotePath, "variables", "variables.data-00000-of-00001"), Path.Combine(varPath, "variables.data-00000-of-00001"));
             Download(Path.Combine(remotePath, "variables", "variables.index"), Path.Combine(varPath, "variables.index"));
@@ -221,7 +221,7 @@ public static IEnumerable<SampleTopicsData> GetTopicsData()
 
         public class SampleTemperatureData
         {
-            public DateTime Date {get; set; }
+            public DateTime Date { get; set; }
             public float Temperature { get; set; }
         }
 
@@ -374,7 +374,7 @@ public class BinaryLabelFloatFeatureVectorSample
             public float[] Features;
         }
 
-        public static  IEnumerable<BinaryLabelFloatFeatureVectorSample> GenerateBinaryLabelFloatFeatureVectorSamples(int exampleCount)
+        public static IEnumerable<BinaryLabelFloatFeatureVectorSample> GenerateBinaryLabelFloatFeatureVectorSamples(int exampleCount)
         {
             var rnd = new Random(0);
             var data = new List<BinaryLabelFloatFeatureVectorSample>();
@@ -405,7 +405,7 @@ public class FloatLabelFloatFeatureVectorSample
             public float[] Features;
         }
 
-        public static  IEnumerable<FloatLabelFloatFeatureVectorSample> GenerateFloatLabelFloatFeatureVectorSamples(int exampleCount, double naRate = 0)
+        public static IEnumerable<FloatLabelFloatFeatureVectorSample> GenerateFloatLabelFloatFeatureVectorSamples(int exampleCount, double naRate = 0)
         {
             var rnd = new Random(0);
             var data = new List<FloatLabelFloatFeatureVectorSample>();
@@ -446,17 +446,20 @@ public class FfmExample
             public float[] Field2;
         }
 
-        public static  IEnumerable<FfmExample> GenerateFfmSamples(int exampleCount)
+        public static IEnumerable<FfmExample> GenerateFfmSamples(int exampleCount)
         {
             var rnd = new Random(0);
             var data = new List<FfmExample>();
             for (int i = 0; i < exampleCount; ++i)
             {
                 // Initialize an example with a random label and an empty feature vector.
-                var sample = new FfmExample() { Label = rnd.Next() % 2 == 0,
+                var sample = new FfmExample()
+                {
+                    Label = rnd.Next() % 2 == 0,
                     Field0 = new float[_simpleBinaryClassSampleFeatureLength],
                     Field1 = new float[_simpleBinaryClassSampleFeatureLength],
-                    Field2 = new float[_simpleBinaryClassSampleFeatureLength] };
+                    Field2 = new float[_simpleBinaryClassSampleFeatureLength]
+                };
                 // Fill feature vector according the assigned label.
                 for (int j = 0; j < 10; ++j)
                 {
@@ -546,5 +549,49 @@ public static List<MulticlassClassificationExample> GenerateRandomMulticlassClas
             }
             return examples;
         }
+
+        // The following variables defines the shape of a matrix. Its shape is _synthesizedMatrixRowCount-by-_synthesizedMatrixColumnCount.
+        // Because in ML.NET key type's minimal value is zero, the first row index is always zero in C# data structure (e.g., MatrixColumnIndex=0
+        // and MatrixRowIndex=0 in MatrixElement below specifies the value at the upper-left corner in the training matrix). If user's row index
+        // starts with 1, their row index 1 would be mapped to the 2nd row in matrix factorization module and their first row may contain no values.
+        // This behavior is also true to column index.
+        private const int _synthesizedMatrixFirstColumnIndex = 1;
+        private const int _synthesizedMatrixFirstRowIndex = 1;
+        private const int _synthesizedMatrixColumnCount = 60;
+        private const int _synthesizedMatrixRowCount = 100;
+
+        // A data structure used to encode a single value in matrix
+        public class MatrixElement
+        {
+            // Matrix column index is at most _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex.
+            [KeyType(Count = _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex)]
+            public uint MatrixColumnIndex;
+            // Matrix row index is at most _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex.
+            [KeyType(Count = _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex)]
+            public uint MatrixRowIndex;
+            // The value at the column MatrixColumnIndex and row MatrixRowIndex.
+            public float Value;
+        }
+
+        // A data structure used to encode prediction result. Comparing with MatrixElement, The field Value in MatrixElement is
+        // renamed to Score because Score is the default name of matrix factorization's output.
+        public class MatrixElementForScore
+        {
+            [KeyType(Count = _synthesizedMatrixColumnCount + _synthesizedMatrixFirstColumnIndex)]
+            public uint MatrixColumnIndex;
+            [KeyType(Count = _synthesizedMatrixRowCount + _synthesizedMatrixFirstRowIndex)]
+            public uint MatrixRowIndex;
+            public float Score;
+        }
+
+        // Create an in-memory matrix as a list of tuples (column index, row index, value).
+        public static List<MatrixElement> GetRecommendationData()
+        {
+            var dataMatrix = new List<MatrixElement>();
+            for (uint i = _synthesizedMatrixFirstColumnIndex; i < _synthesizedMatrixFirstColumnIndex + _synthesizedMatrixColumnCount; ++i)
+                for (uint j = _synthesizedMatrixFirstRowIndex; j < _synthesizedMatrixFirstRowIndex + _synthesizedMatrixRowCount; ++j)
+                    dataMatrix.Add(new MatrixElement() { MatrixColumnIndex = i, MatrixRowIndex = j, Value = (i + j) % 5 });
+            return dataMatrix;
+        }
     }
 }
diff --git a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FactorizationMachineTrainer.cs b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FactorizationMachineTrainer.cs
index dcfe680c4d..ad81336f58 100644
--- a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FactorizationMachineTrainer.cs
+++ b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FactorizationMachineTrainer.cs
@@ -511,6 +511,10 @@ public FieldAwareFactorizationMachinePredictionTransformer Train(IDataView train
 
         public FieldAwareFactorizationMachinePredictionTransformer Fit(IDataView input) => Train(input);
 
+        /// <summary>
+        /// Schema propagation for transformers. Returns the output schema of the data, if
+        /// the input schema is like the one provided.
+        /// </summary>
         public SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
 
diff --git a/src/Microsoft.ML.StaticPipe/MatrixFactorizationStatic.cs b/src/Microsoft.ML.StaticPipe/MatrixFactorizationStatic.cs
index 0ca0f30854..2ecdf6e438 100644
--- a/src/Microsoft.ML.StaticPipe/MatrixFactorizationStatic.cs
+++ b/src/Microsoft.ML.StaticPipe/MatrixFactorizationStatic.cs
@@ -32,7 +32,7 @@ public static class MatrixFactorizationExtensions
         public static Scalar<float> MatrixFactorization<T>(this RegressionCatalog.RegressionTrainers catalog,
             Scalar<float> label, Key<T> matrixColumnIndex, Key<T> matrixRowIndex,
             MatrixFactorizationTrainer.Options options,
-            Action<MatrixFactorizationPredictor> onFit = null)
+            Action<MatrixFactorizationModelParameters> onFit = null)
         {
             Contracts.CheckValue(label, nameof(label));
             Contracts.CheckValue(matrixColumnIndex, nameof(matrixColumnIndex));
diff --git a/test/Microsoft.ML.StaticPipelineTesting/Training.cs b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
index 8931f53697..1fdb3bae66 100644
--- a/test/Microsoft.ML.StaticPipelineTesting/Training.cs
+++ b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
@@ -986,7 +986,7 @@ public void MatrixFactorization()
 
             // The parameter that will be into the onFit method below. The obtained predictor will be assigned to this variable
             // so that we will be able to touch it.
-            MatrixFactorizationPredictor pred = null;
+            MatrixFactorizationModelParameters pred = null;
 
             // Create a statically-typed matrix factorization estimator. The MatrixFactorization's input and output defined in MatrixFactorizationStatic
             // tell what (aks a Scalar<float>) is expected. Notice that only one thread is used for deterministic outcome.
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs b/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
index 7eaf3130a4..64179fb039 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
@@ -18,7 +18,7 @@ namespace Microsoft.ML.Tests.TrainerEstimators
     public partial class TrainerEstimators : TestDataPipeBase
     {
         [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
-        public void MatrixFactorization_Estimator()
+        public void MatrixFactorizationEstimator()
         {
             string labelColumnName = "Label";
             string matrixColumnIndexColumnName = "Col";
@@ -39,11 +39,11 @@ public void MatrixFactorization_Estimator()
                 MatrixRowIndexColumnName = matrixRowIndexColumnName,
                 LabelColumnName = labelColumnName,
                 NumIterations = 3,
-                NumThreads = 1, 
-                K = 4,
+                NumThreads = 1,
+                ApproximationRank = 4,
             };
 
-            var est = new MatrixFactorizationTrainer(Env, options);
+            var est = ML.Recommendation().Trainers.MatrixFactorization(options);
 
             TestEstimatorCore(est, data, invalidInput: invalidData);
 
@@ -68,13 +68,14 @@ public void MatrixFactorizationSimpleTrainAndPredict()
             var data = reader.Read(new MultiFileSource(GetDataPath(TestDatasets.trivialMatrixFactorization.trainFilename)));
 
             // Create a pipeline with a single operator.
-            var options = new MatrixFactorizationTrainer.Options {
+            var options = new MatrixFactorizationTrainer.Options
+            {
                 MatrixColumnIndexColumnName = userColumnName,
                 MatrixRowIndexColumnName = itemColumnName,
                 LabelColumnName = labelColumnName,
                 NumIterations = 3,
                 NumThreads = 1, // To eliminate randomness, # of threads must be 1.
-                K = 7,
+                ApproximationRank = 7,
             };
 
             var pipeline = mlContext.Recommendation().Trainers.MatrixFactorization(options);
@@ -82,6 +83,20 @@ public void MatrixFactorizationSimpleTrainAndPredict()
             // Train a matrix factorization model.
             var model = pipeline.Fit(data);
 
+            // Let's validate content of the model.
+            Assert.Equal(model.Model.ApproximationRank, options.ApproximationRank);
+            var leftMatrix = model.Model.LeftFactorMatrix;
+            var rightMatrix = model.Model.RightFactorMatrix;
+            Assert.Equal(leftMatrix.Count, model.Model.NumberOfRows * model.Model.ApproximationRank);
+            Assert.Equal(rightMatrix.Count, model.Model.NumberOfColumns * model.Model.ApproximationRank);
+            // MF produce different matrixes on different platforms, so at least test thier content on windows.
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                Assert.Equal(leftMatrix[0], (double)0.3091519, 5);
+                Assert.Equal(leftMatrix[leftMatrix.Count - 1], (double)0.5639161, 5);
+                Assert.Equal(rightMatrix[0], (double)0.243584976, 5);
+                Assert.Equal(rightMatrix[rightMatrix.Count - 1], (double)0.380032182, 5);
+            }
             // Read the test data set as an IDataView
             var testData = reader.Read(new MultiFileSource(GetDataPath(TestDatasets.trivialMatrixFactorization.testFilename)));
 
@@ -197,7 +212,7 @@ public void MatrixFactorizationInMemoryData()
                 LabelColumnName = nameof(MatrixElement.Value),
                 NumIterations = 10,
                 NumThreads = 1, // To eliminate randomness, # of threads must be 1.
-                K = 32,
+                ApproximationRank = 32,
             };
 
             var pipeline = mlContext.Recommendation().Trainers.MatrixFactorization(options);
@@ -287,8 +302,8 @@ public void MatrixFactorizationInMemoryDataZeroBaseIndex()
                 LabelColumnName = nameof(MatrixElement.Value),
                 NumIterations = 100,
                 NumThreads = 1, // To eliminate randomness, # of threads must be 1.
-                K = 32,
-                Eta = 0.5,
+                ApproximationRank = 32,
+                LearningRate = 0.5,
             };
 
             var pipeline = mlContext.Recommendation().Trainers.MatrixFactorization(options);
@@ -409,7 +424,7 @@ public void OneClassMatrixFactorizationInMemoryDataZeroBaseIndex()
                 NumIterations = 100,
                 NumThreads = 1, // To eliminate randomness, # of threads must be 1.
                 Lambda = 0.025, // Let's test non-default regularization coefficient.
-                K = 16,
+                ApproximationRank = 16,
                 Alpha = 0.01, // Importance coefficient of loss function over matrix elements not specified in the input matrix.
                 C = 0.15, // Desired value for matrix elements not specified in the input matrix.
             };

From cdc78dfaab7193fe39343d90bc3a771c18632915 Mon Sep 17 00:00:00 2001
From: Marek Linka <mareklinka@users.noreply.github.com>
Date: Sat, 9 Feb 2019 01:06:55 +0100
Subject: [PATCH 04/23] Remove dead code (#2481)

---
 .../Application/DominationLossApplication.cs  | 266 ------------
 .../Application/LogLossApplication.cs         | 169 --------
 .../SizeAdjustedLogLossApplication.cs         | 388 ------------------
 .../Application/WinLossSurplusApplication.cs  | 181 --------
 4 files changed, 1004 deletions(-)
 delete mode 100644 src/Microsoft.ML.FastTree/Application/DominationLossApplication.cs
 delete mode 100644 src/Microsoft.ML.FastTree/Application/LogLossApplication.cs
 delete mode 100644 src/Microsoft.ML.FastTree/Application/SizeAdjustedLogLossApplication.cs
 delete mode 100644 src/Microsoft.ML.FastTree/Application/WinLossSurplusApplication.cs

diff --git a/src/Microsoft.ML.FastTree/Application/DominationLossApplication.cs b/src/Microsoft.ML.FastTree/Application/DominationLossApplication.cs
deleted file mode 100644
index d12a0471fe..0000000000
--- a/src/Microsoft.ML.FastTree/Application/DominationLossApplication.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace Microsoft.ML.Trainers.FastTree.Internal
-{
-#if OLD_DATALOAD
-    public class DominationLossCommandLineArgs : TrainingCommandLineArgs
-    {
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "The smallest HRS label that maps to a positive (default: 1)", ShortName = "pos")]
-        public short smallestPositive = 1;
-    }
-
-    public class DominationLossTrainingApplication : ApplicationBase
-    {
-        DominationLossObjectiveFunction.BestDocsPerQuery TrainSetLabels;
-        new DominationLossCommandLineArgs cmd;
-        private const string RegistrationName = "DominationLossApplication";
-
-        protected override bool IsRankingApplication { get { return true; } }
-        public DominationLossTrainingApplication(IHostEnvironment env, string args, TrainingApplicationData data)
-            : base(env, RegistrationName, args, data)
-        {
-            base.cmd = this.cmd = new DominationLossCommandLineArgs();
-        }
-
-        public override ObjectiveFunction ConstructObjFunc()
-        {
-            return new DominationLossObjectiveFunction(TrainSet, TrainSetLabels, cmd);
-        }
-
-        public override OptimizationAlgorithm ConstructOptimizationAlgorithm(IChannel ch)
-        {
-            OptimizationAlgorithm optimizationAlgorithm = base.ConstructOptimizationAlgorithm(ch);
-            var lossCalculator = new DominationLossTest(optimizationAlgorithm.TrainingScores, TrainSetLabels);
-            optimizationAlgorithm.AdjustTreeOutputsOverride = new LineSearch(lossCalculator, 0, cmd.numPostBracketSteps, cmd.minStepSize);
-            return optimizationAlgorithm;
-        }
-
-        protected override void PrepareLabels(IChannel ch)
-        {
-            TrainSetLabels = new DominationLossObjectiveFunction.BestDocsPerQuery(TrainSet);
-        }
-
-        protected override Test ConstructTestForTrainingData()
-        {
-            return new NDCGTest(ConstructScoreTracker(TrainSet), TrainSet.Ratings, cmd.sortingAlgorithm);
-        }
-
-        protected override void InitializeTests()
-        {
-            if (cmd.testFrequency != int.MaxValue)
-            {
-                AddFullNDCGTests();
-                AddDominationLossTests();
-            }
-
-            // TODO add graph
-
-            // TODO add early stopping
-        }
-
-        private void AddFullNDCGTests()
-        {
-            Tests.Add(new NDCGTest(ConstructScoreTracker(TrainSet), TrainSet.Ratings, cmd.sortingAlgorithm));
-
-            if (ValidSet != null)
-                Tests.Add(new NDCGTest(ConstructScoreTracker(ValidSet), ValidSet.Ratings, cmd.sortingAlgorithm));
-
-            for (int t = 0; TestSets != null && t < TestSets.Length; ++t)
-                Tests.Add(new NDCGTest(ConstructScoreTracker(TestSets[t]), TestSets[t].Ratings, cmd.sortingAlgorithm));
-        }
-
-        private void AddDominationLossTests()
-        {
-            Tests.Add(new DominationLossTest(ConstructScoreTracker(TrainSet), TrainSetLabels));
-
-            if (ValidSet != null)
-            {
-                var labels = new DominationLossObjectiveFunction.BestDocsPerQuery(ValidSet);
-                Tests.Add(new DominationLossTest(ConstructScoreTracker(ValidSet), labels));
-            }
-
-            for (int t = 0; TestSets != null && t < TestSets.Length; ++t)
-            {
-                var labels = new DominationLossObjectiveFunction.BestDocsPerQuery(TestSets[t]);
-                Tests.Add(new DominationLossTest(ConstructScoreTracker(TestSets[t]), labels));
-            }
-        }
-    }
-
-    public class DominationLossObjectiveFunction : ObjectiveFunction
-    {
-        public class BestDocsPerQuery
-        {
-            public int[] BestDocs;
-            public int[] SecondBestDocs;
-            public BestDocsPerQuery(Dataset set)
-            {
-                BestDocs = new int[set.NumQueries];
-                SecondBestDocs = new int[set.NumQueries];
-
-                for (int q = 0; q < set.NumQueries; ++q)
-                {
-                    int best = set.Boundaries[q];
-                    int secondBest = set.Boundaries[q];
-                    short max = -1;
-                    short secondMax = -1;
-
-                    for (int d = set.Boundaries[q]; d < set.Boundaries[q + 1]; ++d)
-                    {
-                        if (max < set.Ratings[d])
-                        {
-                            secondMax = max;
-                            secondBest = best;
-                            max = set.Ratings[d];
-                            best = d;
-                        }
-                        else if (secondMax < set.Ratings[d])
-                        {
-                            secondMax = set.Ratings[d];
-                            secondBest = d;
-                        }
-                    }
-                    BestDocs[q] = best;
-                    SecondBestDocs[q] = secondBest;
-                }
-            }
-        }
-
-        private BestDocsPerQuery _bestDocsPerQuery;
-
-        public DominationLossObjectiveFunction(Dataset trainSet, BestDocsPerQuery bestDocsPerQuery, DominationLossCommandLineArgs cmd)
-            : base(
-                trainSet,
-                cmd.learningRates,
-                cmd.shrinkage,
-                cmd.maxTreeOutput,
-                cmd.getDerivativesSampleRate,
-                cmd.bestStepRankingRegressionTrees,
-                cmd.rngSeed)
-        {
-            _bestDocsPerQuery = bestDocsPerQuery;
-        }
-
-        protected override void GetGradientInOneQuery(int query, int threadIndex)
-        {
-            int begin = Dataset.Boundaries[query];
-            int end = Dataset.Boundaries[query + 1];
-
-            if (end - begin <= 1)
-                return;
-
-            int bestDoc = _bestDocsPerQuery.BestDocs[query];
-            int secondBestDoc = _bestDocsPerQuery.SecondBestDocs[query];
-
-            // find max score
-            double max = double.NegativeInfinity;
-            double maxNotBest = double.NegativeInfinity;
-
-            for (int d = begin; d < end; ++d)
-            {
-                if (max < _scores[d])
-                    max = _scores[d];
-                if (d != bestDoc && maxNotBest < _scores[d])
-                    maxNotBest = _scores[d];
-            }
-
-            // sum of exponents and sum of all but best
-            double sum = 0.0;
-            double sumAllButBest = 0.0;
-            for (int d = begin; d < end; ++d)
-            {
-                sum += Math.Exp(_scores[d] - max);
-                if (d != bestDoc)
-                    sumAllButBest += Math.Exp(_scores[d] - maxNotBest);
-            }
-
-            // calculate gradients
-            for (int d = begin; d < end; ++d)
-            {
-                _gradient[d] = _learningRate * (-Math.Exp(_scores[d] - max) / sum - 0.5 * Math.Exp(_scores[d] - maxNotBest) / sumAllButBest);
-            }
-
-            _gradient[bestDoc] = _learningRate * (1.0 - Math.Exp(_scores[bestDoc] - max) / sum);
-            _gradient[secondBestDoc] += _learningRate * 0.5;
-        }
-    }
-
-    public class DominationLossTest : Test
-    {
-        DominationLossObjectiveFunction.BestDocsPerQuery _bestDocsPerQuery;
-        public DominationLossTest(ScoreTracker scoreTracker, DominationLossObjectiveFunction.BestDocsPerQuery bestDocsPerQuery)
-            : base(scoreTracker)
-        {
-            _bestDocsPerQuery = bestDocsPerQuery;
-            Contracts.Check(scoreTracker.Dataset.NumQueries == bestDocsPerQuery.BestDocs.Length, "Mismatch between dataset and labels");
-        }
-
-        public double ComputeDominationLoss(double[] scores)
-        {
-            int chunkSize = 1 + Dataset.NumQueries / BlockingThreadPool.NumThreads;   // Minimizes the number of repeat computations in sparse array to have each thread take as big a chunk as possible
-            double totalOutput = 0.0;
-            var _lock = new Object();
-            for (int queryBegin = 0; queryBegin < Dataset.NumQueries; queryBegin += chunkSize)
-                BlockingThreadPool.RunOrBlock(delegate(int startQuery, int endQuery)
-                {
-                    double output = 0.0;
-                    for (int query = startQuery; query <= endQuery; query++)
-                    {
-                        int begin = Dataset.Boundaries[query];
-                        int end = Dataset.Boundaries[query + 1];
-
-                        if (end - begin <= 1)
-                            continue;
-
-                        int bestDoc = _bestDocsPerQuery.BestDocs[query];
-                        int secondBestDoc = _bestDocsPerQuery.SecondBestDocs[query];
-                        double bestDocScore = scores[bestDoc];
-                        double secondBestDocScore = scores[secondBestDoc];
-
-                        // find max score
-                        double max = double.NegativeInfinity;
-                        double maxNotBest = double.NegativeInfinity;
-
-                        for (int d = begin; d < end; ++d)
-                        {
-                            if (max < scores[d])
-                                max = scores[d];
-                            if (d != bestDoc && maxNotBest < scores[d])
-                                maxNotBest = scores[d];
-                        }
-
-                        // sum of exponents and sum of all but best
-                        double sum = 0.0;
-                        double sumAllButBest = 0.0;
-                        for (int d = begin; d < end; ++d)
-                        {
-                            sum += Math.Exp(scores[d] - max);
-                            if (d != bestDoc)
-                                sumAllButBest += Math.Exp(scores[d] - maxNotBest);
-                        }
-
-                        output += max - bestDocScore + Math.Log(sum) + 0.5 * (maxNotBest - secondBestDocScore + Math.Log(sumAllButBest));
-                    }
-                    lock (_lock)
-                    {
-                        totalOutput += output;
-                    }
-                }, queryBegin, Math.Min(queryBegin + chunkSize - 1, Dataset.NumQueries - 1));
-            BlockingThreadPool.BlockUntilAllWorkItemsFinish();
-            return totalOutput;
-        }
-
-        public override IEnumerable<TestResult> ComputeTests(double[] scores)
-        {
-            List<TestResult> result = new List<TestResult>()
-            {
-                new TestResult("DominationLoss", ComputeDominationLoss(scores), 1, true, TestResult.ValueOperator.Sum),
-            };
-
-            return result;
-        }
-    }
-#endif
-}
diff --git a/src/Microsoft.ML.FastTree/Application/LogLossApplication.cs b/src/Microsoft.ML.FastTree/Application/LogLossApplication.cs
deleted file mode 100644
index 9dd896a401..0000000000
--- a/src/Microsoft.ML.FastTree/Application/LogLossApplication.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace Microsoft.ML.Trainers.FastTree.Internal
-{
-#if OLD_DATALOAD
-    public class LogLossCommandLineArgs : TrainingCommandLineArgs
-    {
-        public enum LogLossMode { Pairwise, Wholepage };
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "Which style of log loss to use", ShortName = "llm")]
-        public LogLossMode loglossmode = LogLossMode.Pairwise;
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "log loss cefficient", ShortName = "llc")]
-        public double loglosscoef = 1.0;
-    }
-
-    public class LogLossTrainingApplication : ApplicationBase
-    {
-        new LogLossCommandLineArgs cmd;
-        private const string RegistrationName = "LogLossApplication";
-
-        public LogLossTrainingApplication(IHostEnvironment env, string args, TrainingApplicationData data)
-            : base(env, RegistrationName, args, data)
-        {
-            base.cmd = this.cmd = new LogLossCommandLineArgs();
-        }
-
-        public override ObjectiveFunction ConstructObjFunc()
-        {
-            return new LogLossObjectiveFunction(TrainSet, cmd);
-        }
-
-        public override OptimizationAlgorithm ConstructOptimizationAlgorithm(IChannel ch)
-        {
-            OptimizationAlgorithm optimizationAlgorithm = base.ConstructOptimizationAlgorithm(ch);
-            //            optimizationAlgorithm.AdjustTreeOutputsOverride = new NoOutputOptimization();
-            var lossCalculator = new LogLossTest(optimizationAlgorithm.TrainingScores, cmd.loglosscoef);
-            int lossIndex = (cmd.loglossmode == LogLossCommandLineArgs.LogLossMode.Pairwise) ? 0 : 1;
-
-            optimizationAlgorithm.AdjustTreeOutputsOverride = new LineSearch(lossCalculator, lossIndex, cmd.numPostBracketSteps, cmd.minStepSize);
-
-            return optimizationAlgorithm;
-        }
-
-        protected override void PrepareLabels(IChannel ch)
-        {
-        }
-
-        protected override Test ConstructTestForTrainingData()
-        {
-            return new LogLossTest(ConstructScoreTracker(TrainSet), cmd.loglosscoef);
-        }
-
-        protected override void InitializeTests()
-        {
-            Tests.Add(new LogLossTest(ConstructScoreTracker(TrainSet), cmd.loglosscoef));
-            if (ValidSet != null)
-                Tests.Add(new LogLossTest(ConstructScoreTracker(ValidSet), cmd.loglosscoef));
-
-            if (TestSets != null)
-            {
-                for (int t = 0; t < TestSets.Length; ++t)
-                {
-                    Tests.Add(new LogLossTest(ConstructScoreTracker(TestSets[t]), cmd.loglosscoef));
-                }
-            }
-        }
-    }
-
-    public class LogLossObjectiveFunction : RankingObjectiveFunction
-    {
-        private LogLossCommandLineArgs.LogLossMode _mode;
-        private double _coef = 1.0;
-
-        public LogLossObjectiveFunction(Dataset trainSet, LogLossCommandLineArgs cmd)
-            : base(trainSet, trainSet.Ratings, cmd)
-        {
-            _mode = cmd.loglossmode;
-            _coef = cmd.loglosscoef;
-        }
-
-        protected override void GetGradientInOneQuery(int query, int threadIndex)
-        {
-            int begin = Dataset.Boundaries[query];
-            int end = Dataset.Boundaries[query + 1];
-            short[] labels = Dataset.Ratings;
-
-            if (end - begin <= 1)
-                return;
-            Array.Clear(_gradient, begin, end - begin);
-
-            for (int d1 = begin; d1 < end - 1; ++d1)
-            {
-                int stop = (_mode == LogLossCommandLineArgs.LogLossMode.Pairwise) ? d1 + 2 : end;
-                for (int d2 = d1 + 1; d2 < stop; ++d2)
-                {
-                    short labelDiff = (short)(labels[d1] - labels[d2]);
-                    if (labelDiff == 0)
-                        continue;
-                    double delta = (_coef * labelDiff) / (1.0 + Math.Exp(_coef * labelDiff * (_scores[d1] - _scores[d2])));
-
-                    _gradient[d1] += delta;
-                    _gradient[d2] -= delta;
-                }
-            }
-        }
-    }
-
-    public class LogLossTest : Test
-    {
-        protected double _coef;
-        public LogLossTest(ScoreTracker scoreTracker, double coef)
-            : base(scoreTracker)
-        {
-            _coef = coef;
-        }
-
-        public override IEnumerable<TestResult> ComputeTests(double[] scores)
-        {
-            Object _lock = new Object();
-            double pairedLoss = 0.0;
-            double allPairLoss = 0.0;
-            short maxLabel = 0;
-            short minLabel = 10000;
-
-            for (int query = 0; query < Dataset.Boundaries.Length - 1; query++)
-            {
-                int start = Dataset.Boundaries[query];
-                int length = Dataset.Boundaries[query + 1] - start;
-                for (int i = start; i < start + length; i++)
-                    for (int j = i + 1; j < start + length; j++)
-                        allPairLoss += Math.Log((1.0 + Math.Exp(-_coef * (Dataset.Ratings[i] - Dataset.Ratings[j]) * (scores[i] - scores[j]))));
-                //allPairLoss += Math.Max(0.0, _coef - (Dataset.Ratings[i] - Dataset.Ratings[j]) * (scores[i] - scores[j]));
-
-                for (int i = start; i < start + length - 1; i++)
-                {
-                    pairedLoss += Math.Log((1.0 + Math.Exp(-_coef * (Dataset.Ratings[i] - Dataset.Ratings[i + 1]) * (scores[i] - scores[i + 1]))));
-                    //                     pairedLoss += Math.Max(0.0, _coef - (Dataset.Ratings[i] - Dataset.Ratings[i + 1]) * (scores[i] - scores[i + 1]));
-                }
-            }
-
-            for (int i = 0; i < Dataset.Ratings.Length; i++)
-            {
-                if (Dataset.Ratings[i] > maxLabel)
-                    maxLabel = Dataset.Ratings[i];
-                if (Dataset.Ratings[i] < minLabel)
-                    minLabel = Dataset.Ratings[i];
-            }
-            List<TestResult> result = new List<TestResult>()
-            {
-                new TestResult("paired loss", pairedLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("all pairs loss", allPairLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("coefficient", _coef, 1, true, TestResult.ValueOperator.Constant),
-                new TestResult("max Label", maxLabel, 1, false, TestResult.ValueOperator.Max),
-                new TestResult("min Label", minLabel, 1, true, TestResult.ValueOperator.Min),
-            };
-
-            return result;
-        }
-    }
-
-    public class NoOutputOptimization : IStepSearch
-    {
-        public NoOutputOptimization() { }
-
-        public void AdjustTreeOutputs(IChannel ch, RegressionTree tree, DocumentPartitioning partitioning, ScoreTracker trainingScores) { }
-    }
-#endif
-}
diff --git a/src/Microsoft.ML.FastTree/Application/SizeAdjustedLogLossApplication.cs b/src/Microsoft.ML.FastTree/Application/SizeAdjustedLogLossApplication.cs
deleted file mode 100644
index 0cb8c9496f..0000000000
--- a/src/Microsoft.ML.FastTree/Application/SizeAdjustedLogLossApplication.cs
+++ /dev/null
@@ -1,388 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace Microsoft.ML.Trainers.FastTree.Internal
-{
-#if OLD_DATALOAD
-    public class SizeAdjustedLogLossCommandLineArgs : TrainingCommandLineArgs
-    {
-        public enum LogLossMode
-        {
-            Pairwise,
-            Wholepage
-        };
-
-        public enum CostFunctionMode
-        {
-            SizeAdjustedWinratePredictor,
-            SizeAdjustedPageOrdering
-        };
-
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "Which style of log loss to use", ShortName = "llm")]
-        public LogLossMode loglossmode = LogLossMode.Pairwise;
-
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "Which style of cost function to use", ShortName = "cfm")]
-        public CostFunctionMode costFunctionMode = CostFunctionMode.SizeAdjustedPageOrdering;
-
-        // REVIEW: If we ever want to expose this application in TLC, the natural thing would be for these to
-        // be loaded from a column from the input data view. However I'll keep them around for now, so that when we
-        // do migrate there's some clear idea of how it is to be done.
-        [Argument(ArgumentType.AtMostOnce, HelpText = "tab seperated file which contains the max and min score from the model", ShortName = "srange")]
-        public string scoreRangeFileName = null;
-
-        [Argument(ArgumentType.MultipleUnique, HelpText = "TSV filename with float size values associated with train bin file", ShortName = "trsl")]
-        public string[] trainSizeLabelFilenames = null;
-
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "TSV filename with float size values associated with validation bin file", ShortName = "vasl")]
-        public string validSizeLabelFilename = null;
-
-        [Argument(ArgumentType.MultipleUnique, HelpText = "TSV filename with float size values associated with test bin file", ShortName = "tesl")]
-        public string[] testSizeLabelFilenames = null;
-
-        [Argument(ArgumentType.LastOccurenceWins, HelpText = "log loss cefficient", ShortName = "llc")]
-        public double loglosscoef = 1.0;
-
-    }
-
-    public class SizeAdjustedLogLossUtil
-    {
-        public const string sizeLabelName = "size";
-
-        public static float[] GetSizeLabels(Dataset set)
-        {
-            if (set == null)
-            {
-                return null;
-            }
-            float[] labels = set.Skeleton.GetData<float>(sizeLabelName);
-            if (labels == null)
-            {
-                labels = set.Ratings.Select(x => (float)x).ToArray();
-            }
-            return labels;
-        }
-    }
-
-    public class SizeAdjustedLogLossTrainingApplication : ApplicationBase
-    {
-        new SizeAdjustedLogLossCommandLineArgs cmd;
-        float[] trainSetSizeLabels;
-        private const string RegistrationName = "SizeAdjustedLogLossApplication";
-
-        public SizeAdjustedLogLossTrainingApplication(IHostEnvironment env, string args, TrainingApplicationData data)
-            : base(env, RegistrationName, args, data)
-        {
-            base.cmd = this.cmd = new SizeAdjustedLogLossCommandLineArgs();
-        }
-
-        public override ObjectiveFunction ConstructObjFunc()
-        {
-            return new SizeAdjustedLogLossObjectiveFunction(TrainSet, cmd);
-        }
-
-        public override OptimizationAlgorithm ConstructOptimizationAlgorithm(IChannel ch)
-        {
-            OptimizationAlgorithm optimizationAlgorithm = base.ConstructOptimizationAlgorithm(ch);
-            //          optimizationAlgorithm.AdjustTreeOutputsOverride = new NoOutputOptimization(); // For testing purposes - this will not use line search and thus the scores won't be scaled.
-            var lossCalculator = new SizeAdjustedLogLossTest(optimizationAlgorithm.TrainingScores, cmd.scoreRangeFileName, trainSetSizeLabels, cmd.loglosscoef);
-
-            // The index of the label signifies which index from TestResult would be used as a loss. For every query, we compute both wholepage and pairwise loss with the two cost function modes, this index lets us pick the appropriate one.
-            int lossIndex = 0;
-            if (cmd.loglossmode == SizeAdjustedLogLossCommandLineArgs.LogLossMode.Wholepage && cmd.costFunctionMode == SizeAdjustedLogLossCommandLineArgs.CostFunctionMode.SizeAdjustedPageOrdering)
-            {
-                lossIndex = 1;
-            }
-            else if (cmd.loglossmode == SizeAdjustedLogLossCommandLineArgs.LogLossMode.Pairwise && cmd.costFunctionMode == SizeAdjustedLogLossCommandLineArgs.CostFunctionMode.SizeAdjustedWinratePredictor)
-            {
-                lossIndex = 2;
-            }
-            else if (cmd.loglossmode == SizeAdjustedLogLossCommandLineArgs.LogLossMode.Wholepage && cmd.costFunctionMode == SizeAdjustedLogLossCommandLineArgs.CostFunctionMode.SizeAdjustedWinratePredictor)
-            {
-                lossIndex = 3;
-            }
-
-            optimizationAlgorithm.AdjustTreeOutputsOverride = new LineSearch(lossCalculator, lossIndex, cmd.numPostBracketSteps, cmd.minStepSize);
-
-            return optimizationAlgorithm;
-        }
-
-        private static IEnumerable<float> LoadSizeLabels(string filename)
-        {
-            using (StreamReader reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)))
-            {
-                string line = reader.ReadLine();
-                Contracts.Check(line != null && line.Trim() == "m:Size", "Regression label file should contain only one column m:Size");
-                while ((line = reader.ReadLine()) != null)
-                {
-                    float val = float.Parse(line.Trim(), CultureInfo.InvariantCulture);
-                    yield return val;
-                }
-            }
-        }
-
-        protected override void PrepareLabels(IChannel ch)
-        {
-            trainSetSizeLabels = SizeAdjustedLogLossUtil.GetSizeLabels(TrainSet);
-        }
-
-        protected override Test ConstructTestForTrainingData()
-        {
-            return new SizeAdjustedLogLossTest(ConstructScoreTracker(TrainSet), cmd.scoreRangeFileName, SizeAdjustedLogLossUtil.GetSizeLabels(TrainSet), cmd.loglosscoef);
-        }
-
-        protected override void ProcessBinFile(int trainValidTest, int index, DatasetBinFile bin)
-        {
-            string labelPath = null;
-            switch (trainValidTest)
-            {
-            case 0:
-                if (cmd.trainSizeLabelFilenames != null && index < cmd.trainSetFilenames.Length)
-                {
-                    labelPath = cmd.trainSizeLabelFilenames[index];
-                }
-                break;
-            case 1:
-                if (cmd.validSizeLabelFilename != null)
-                {
-                    labelPath = cmd.validSizeLabelFilename;
-                }
-                break;
-            case 2:
-                if (cmd.testSizeLabelFilenames != null && index < cmd.testSizeLabelFilenames.Length)
-                {
-                    labelPath = cmd.testSizeLabelFilenames[index];
-                }
-                break;
-            }
-            // If we have no labels, return.
-            if (labelPath == null)
-            {
-                return;
-            }
-            float[] labels = LoadSizeLabels(labelPath).ToArray();
-            bin.DatasetSkeleton.SetData(SizeAdjustedLogLossUtil.sizeLabelName, labels, false);
-        }
-
-        protected override void InitializeTests()
-        {
-            Tests.Add(new SizeAdjustedLogLossTest(ConstructScoreTracker(TrainSet), cmd.scoreRangeFileName, SizeAdjustedLogLossUtil.GetSizeLabels(TrainSet), cmd.loglosscoef));
-            if (ValidSet != null)
-            {
-                Tests.Add(new SizeAdjustedLogLossTest(ConstructScoreTracker(ValidSet), cmd.scoreRangeFileName, SizeAdjustedLogLossUtil.GetSizeLabels(ValidSet), cmd.loglosscoef));
-            }
-
-            if (TestSets != null && TestSets.Length > 0)
-            {
-                for (int t = 0; t < TestSets.Length; ++t)
-                {
-                    Tests.Add(new SizeAdjustedLogLossTest(ConstructScoreTracker(TestSets[t]), cmd.scoreRangeFileName, SizeAdjustedLogLossUtil.GetSizeLabels(TestSets[t]), cmd.loglosscoef));
-                }
-            }
-        }
-
-        public override void PrintIterationMessage(IChannel ch, IProgressChannel pch)
-        {
-            base.PrintIterationMessage(ch, pch);
-        }
-    }
-
-    public class SizeAdjustedLogLossObjectiveFunction : RankingObjectiveFunction
-    {
-        private SizeAdjustedLogLossCommandLineArgs.LogLossMode _mode;
-        private SizeAdjustedLogLossCommandLineArgs.CostFunctionMode _algo;
-        private double _llc;
-
-        public SizeAdjustedLogLossObjectiveFunction(Dataset trainSet, SizeAdjustedLogLossCommandLineArgs cmd)
-            : base(trainSet, trainSet.Ratings, cmd)
-        {
-            _mode = cmd.loglossmode;
-            _algo = cmd.costFunctionMode;
-            _llc = cmd.loglosscoef;
-        }
-
-        protected override void GetGradientInOneQuery(int query, int threadIndex)
-        {
-            int begin = Dataset.Boundaries[query];
-            int end = Dataset.Boundaries[query + 1];
-            short[] labels = Dataset.Ratings;
-            float[] sizes = SizeAdjustedLogLossUtil.GetSizeLabels(Dataset);
-
-            Contracts.Check(Dataset.NumDocs == sizes.Length, "Mismatch between dataset and labels");
-
-            if (end - begin <= 1)
-            {
-                return;
-            }
-            Array.Clear(_gradient, begin, end - begin);
-
-            for (int d1 = begin; d1 < end - 1; ++d1)
-            {
-                for (int d2 = d1 + 1; d2 < end; ++d2)
-                {
-                    float size = sizes[d1];
-
-                    //Compute Lij
-                    float sizeAdjustedLoss = 0.0F;
-                    for (int d3 = d2; d3 < end; ++d3)
-                    {
-                        size -= sizes[d3];
-                        if (size >= 0.0F && labels[d3] > 0)
-                        {
-                            sizeAdjustedLoss = 1.0F;
-                        }
-                        else if (size < 0.0F && labels[d3] > 0)
-                        {
-                            sizeAdjustedLoss = (1.0F + (size / sizes[d3]));
-                        }
-
-                        if (size <= 0.0F || sizeAdjustedLoss > 0.0F)
-                        {
-                            // Exit condition- we have reached size or size adjusted loss is already populated.
-                            break;
-                        }
-                    }
-
-                    double scoreDiff = _scores[d1] - _scores[d2];
-                    float labelDiff = ((float)labels[d1] - sizeAdjustedLoss);
-                    double delta = 0.0;
-                    if (_algo == SizeAdjustedLogLossCommandLineArgs.CostFunctionMode.SizeAdjustedPageOrdering)
-                    {
-                        delta = (_llc * labelDiff) / (1.0 + Math.Exp(_llc * labelDiff * scoreDiff));
-                    }
-                    else
-                    {
-                        delta = (double)labels[d1] - ((double)(labels[d1] + sizeAdjustedLoss) / (1.0 + Math.Exp(-scoreDiff)));
-                    }
-
-                    _gradient[d1] += delta;
-                    _gradient[d2] -= delta;
-
-                    if (_mode == SizeAdjustedLogLossCommandLineArgs.LogLossMode.Pairwise)
-                    {
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    public class SizeAdjustedLogLossTest : Test
-    {
-        protected string _scoreRangeFileName = null;
-        static double maxScore = double.MinValue;
-        static double minScore = double.MaxValue;
-        private float[] _sizeLabels;
-        private double _llc;
-
-        public SizeAdjustedLogLossTest(ScoreTracker scoreTracker, string scoreRangeFileName, float[] sizeLabels, double loglossCoeff)
-            : base(scoreTracker)
-        {
-            Contracts.Check(scoreTracker.Dataset.NumDocs == sizeLabels.Length, "Mismatch between dataset and labels");
-            _sizeLabels = sizeLabels;
-            _scoreRangeFileName = scoreRangeFileName;
-            _llc = loglossCoeff;
-        }
-
-        public override IEnumerable<TestResult> ComputeTests(double[] scores)
-        {
-            Object _lock = new Object();
-            double pairedPageOrderingLoss = 0.0;
-            double allPairPageOrderingLoss = 0.0;
-            double pairedSAWRPredictLoss = 0.0;
-            double allPairSAWRPredictLoss = 0.0;
-
-            short maxLabel = 0;
-            short minLabel = 10000;
-            for (int query = 0; query < Dataset.Boundaries.Length - 1; ++query)
-            {
-                int begin = Dataset.Boundaries[query];
-                int end = Dataset.Boundaries[query + 1];
-
-                if (end - begin <= 1)
-                {
-                    continue;
-                }
-
-                for (int d1 = begin; d1 < end - 1; ++d1)
-                {
-                    bool firstTime = false;
-                    for (int d2 = d1 + 1; d2 < end; ++d2)
-                    {
-                        float size = _sizeLabels[d1];
-
-                        //Compute Lij
-                        float sizeAdjustedLoss = 0.0F;
-                        for (int d3 = d2; d3 < end; ++d3)
-                        {
-                            size -= _sizeLabels[d3];
-                            if (size >= 0.0F && Dataset.Ratings[d3] > 0)
-                            {
-                                sizeAdjustedLoss = 1.0F;
-                            }
-                            else if (size < 0.0F && Dataset.Ratings[d3] > 0)
-                            {
-                                sizeAdjustedLoss = (1.0F + (size / _sizeLabels[d3]));
-                            }
-
-                            if (size <= 0.0F || sizeAdjustedLoss > 0.0F)
-                            {
-                                // Exit condition- we have reached size or size adjusted loss is already populated.
-                                break;
-                            }
-                        }
-                        //Compute page ordering loss
-                        double scoreDiff = scores[d1] - scores[d2];
-                        float labelDiff = ((float)Dataset.Ratings[d1] - sizeAdjustedLoss);
-                        double pageOrderingLoss = Math.Log(1.0 + Math.Exp(-_llc * labelDiff * scoreDiff));
-
-                        // Compute SAWR predict loss
-                        double sawrPredictLoss = (double)((Dataset.Ratings[d1] + sizeAdjustedLoss) * Math.Log(1.0 + Math.Exp(scoreDiff))) - ((double)Dataset.Ratings[d1] * scoreDiff);
-                        if (!firstTime)
-                        {
-                            pairedPageOrderingLoss += pageOrderingLoss;
-                            pairedSAWRPredictLoss += sawrPredictLoss;
-                            firstTime = true;
-                        }
-                        allPairPageOrderingLoss += pageOrderingLoss;
-                        allPairSAWRPredictLoss += sawrPredictLoss;
-                    }
-                }
-            }
-
-            for (int i = 0; i < Dataset.Ratings.Length; i++)
-            {
-                if (Dataset.Ratings[i] > maxLabel)
-                    maxLabel = Dataset.Ratings[i];
-                if (Dataset.Ratings[i] < minLabel)
-                    minLabel = Dataset.Ratings[i];
-                if (scores[i] > maxScore)
-                    maxScore = scores[i];
-                if (scores[i] < minScore)
-                    minScore = scores[i];
-            }
-
-            if (_scoreRangeFileName != null)
-            {
-                using (StreamWriter sw = File.CreateText(_scoreRangeFileName))
-                {
-                    sw.WriteLine(string.Format("{0}\t{1}", minScore, maxScore));
-                }
-            }
-
-            List<TestResult> result = new List<TestResult>()
-            {
-                // The index of the label signifies which index from TestResult would be used as a loss. For every query, we compute both wholepage and pairwise loss with the two cost function modes, this index lets us pick the appropriate one.
-                new TestResult("page ordering paired loss", pairedPageOrderingLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("page ordering all pairs loss", allPairPageOrderingLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("SAWR predict paired loss", pairedSAWRPredictLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("SAWR predict all pairs loss", allPairSAWRPredictLoss, Dataset.NumDocs, true, TestResult.ValueOperator.Average),
-                new TestResult("max Label", maxLabel, 1, false, TestResult.ValueOperator.Max),
-                new TestResult("min Label", minLabel, 1, true, TestResult.ValueOperator.Min),
-            };
-
-            return result;
-        }
-    }
-#endif
-}
diff --git a/src/Microsoft.ML.FastTree/Application/WinLossSurplusApplication.cs b/src/Microsoft.ML.FastTree/Application/WinLossSurplusApplication.cs
deleted file mode 100644
index 1708033161..0000000000
--- a/src/Microsoft.ML.FastTree/Application/WinLossSurplusApplication.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace Microsoft.ML.Trainers.FastTree.Internal
-{
-#if OLD_DATALOAD
-    public class WinLossSurplusCommandLineArgs : TrainingCommandLineArgs
-    {
-        [Argument(ArgumentType.AtMostOnce, HelpText = "Scaling Factor for win loss surplus", ShortName = "wls")]
-        public double winlossScaleFactor = 1.0;
-    }
-
-    public class WinLossSurplusTrainingApplication : RankingApplication
-    {
-        new WinLossSurplusCommandLineArgs cmd;
-        private const string RegistrationName = "WinLossSurplusApplication";
-
-        public WinLossSurplusTrainingApplication(IHostEnvironment env, string args, TrainingApplicationData data)
-            : base(env, RegistrationName, args, data)
-        {
-            base.cmd = this.cmd = new WinLossSurplusCommandLineArgs();
-        }
-
-        public override ObjectiveFunction ConstructObjFunc()
-        {
-            return new WinLossSurplusObjectiveFunction(TrainSet, TrainSet.Ratings, cmd);
-        }
-
-        public override OptimizationAlgorithm ConstructOptimizationAlgorithm(IChannel ch)
-        {
-            OptimizationAlgorithm optimizationAlgorithm = base.ConstructOptimizationAlgorithm(ch);
-            var lossCalculator = new WinLossSurplusTest(optimizationAlgorithm.TrainingScores, TrainSet.Ratings, cmd.sortingAlgorithm, cmd.winlossScaleFactor);
-            optimizationAlgorithm.AdjustTreeOutputsOverride = new LineSearch(lossCalculator, 0, cmd.numPostBracketSteps, cmd.minStepSize);
-            return optimizationAlgorithm;
-        }
-
-        protected override Test ConstructTestForTrainingData()
-        {
-            return new WinLossSurplusTest(ConstructScoreTracker(TrainSet), TrainSet.Ratings, cmd.sortingAlgorithm, cmd.winlossScaleFactor);
-        }
-
-        public override void PrintIterationMessage(IChannel ch, IProgressChannel pch)
-        {
-            // REVIEW: Shift this to use progress channels.
-#if OLD_TRACING
-            if (PruningTest != null)
-            {
-                if (PruningTest is TestWindowWithTolerance)
-                {
-                    if (PruningTest.BestIteration != -1)
-                        ch.Info("Iteration {0} \t(Best tolerated validation moving average WinLossSurplus {1}:{2:00.00}~{3:00.00})",
-                                Ensemble.NumTrees,
-                                PruningTest.BestIteration,
-                                (PruningTest as TestWindowWithTolerance).BestAverageValue,
-                                (PruningTest as TestWindowWithTolerance).CurrentAverageValue);
-                    else
-                        ch.Info("Iteration {0}", Ensemble.NumTrees);
-                }
-                else
-                {
-                    ch.Info("Iteration {0} \t(best validation WinLoss {1}:{2:00.00}>{3:00.00})",
-                            Ensemble.NumTrees,
-                            PruningTest.BestIteration,
-                            PruningTest.BestResult.FinalValue,
-                            PruningTest.ComputeTests().First().FinalValue);
-                }
-            }
-            else
-                base.PrintIterationMessage(ch, pch);
-#else
-            base.PrintIterationMessage(ch, pch);
-#endif
-        }
-
-        protected override Test CreateStandardTest(Dataset dataset)
-        {
-            return new WinLossSurplusTest(
-                ConstructScoreTracker(dataset),
-                dataset.Ratings,
-                cmd.sortingAlgorithm,
-                cmd.winlossScaleFactor);
-        }
-
-        protected override Test CreateSpecialTrainSetTest()
-        {
-            return CreateStandardTest(TrainSet);
-        }
-
-        protected override Test CreateSpecialValidSetTest()
-        {
-            return CreateStandardTest(ValidSet);
-        }
-
-        protected override string GetTestGraphHeader()
-        {
-            return "Eval:\tFileName\tMaxSurplus\tSurplus@100\tSurplus@200\tSurplus@300\tSurplus@400\tSurplus@500\tSurplus@1000\tMaxSurplusPos\tPercentTop\n";
-        }
-    }
-
-    public class WinLossSurplusObjectiveFunction : LambdaRankObjectiveFunction
-    {
-        public WinLossSurplusObjectiveFunction(Dataset trainSet, short[] labels, WinLossSurplusCommandLineArgs cmd)
-            : base(trainSet, labels, cmd)
-        {
-        }
-
-        protected override void GetGradientInOneQuery(int query, int threadIndex)
-        {
-            int begin = Dataset.Boundaries[query];
-            int numDocuments = Dataset.Boundaries[query + 1] - Dataset.Boundaries[query];
-
-            Array.Clear(_gradient, begin, numDocuments);
-            Array.Clear(_weights, begin, numDocuments);
-
-            double inverseMaxDCG = _inverseMaxDCGT[query];
-
-            int[] permutation = _permutationBuffers[threadIndex];
-
-            short[] labels = Labels;
-            double[] scoresToUse = _scores;
-
-            // Keep track of top 3 labels for later use
-            //GetTopQueryLabels(query, permutation, false);
-            unsafe
-            {
-                fixed (int* pPermutation = permutation)
-                fixed (short* pLabels = labels)
-                fixed (double* pScores = scoresToUse, pLambdas = _gradient, pWeights = _weights, pDiscount = _discount)
-                fixed (double* pGain = _gain, pGainLabels = _gainLabels, pSigmoidTable = _sigmoidTable)
-                fixed (int* pOneTwoThree = _oneTwoThree)
-                {
-                    // calculates the permutation that orders "scores" in descending order, without modifying "scores"
-                    Array.Copy(_oneTwoThree, permutation, numDocuments);
-#if USE_FASTTREENATIVE
-                    double lambdaSum = 0;
-
-                    C_Sort(pPermutation, &pScores[begin], &pLabels[begin], numDocuments);
-
-                    // Keep track of top 3 labels for later use
-                    GetTopQueryLabels(query, permutation, true);
-
-                    int numActualResults = numDocuments;
-
-                    C_GetSurplusDerivatives(numDocuments, begin, pPermutation, pLabels,
-                            pScores, pLambdas, pWeights, pDiscount,
-                            pGainLabels,
-                            pSigmoidTable, _minScore, _maxScore, _sigmoidTable.Length, _scoreToSigmoidTableFactor,
-                            _costFunctionParam, _distanceWeight2, &lambdaSum, double.MinValue);
-
-                    if (_normalizeQueryLambdas)
-                    {
-                        if (lambdaSum > 0)
-                        {
-                            double normFactor = (10 * Math.Log(1 + lambdaSum)) / lambdaSum;
-
-                            for (int i = begin; i < begin + numDocuments; ++i)
-                            {
-                                pLambdas[i] = pLambdas[i] * normFactor;
-                                pWeights[i] = pWeights[i] * normFactor;
-                            }
-                        }
-                    }
-#else
-                    throw new Exception("Shifted NDCG / ContinuousWeightedRanknet / WinLossSurplus / distanceWeight2 / normalized lambdas are only supported by unmanaged code");
-#endif
-                }
-            }
-        }
-
-        [DllImport("FastTreeNative", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
-        private unsafe static extern void C_GetSurplusDerivatives(int numDocuments, int begin, int* pPermutation, short* pLabels,
-                                                           double* pScores, double* pLambdas, double* pWeights, double* pDiscount,
-                                                           double* pGainLabels, double* lambdaTable, double minScore, double maxScore,
-                                                           int lambdaTableLength, double scoreToLambdaTableFactor,
-                                                           char costFunctionParam, [MarshalAs(UnmanagedType.U1)] bool distanceWeight2,
-                                                           double* pLambdaSum, double doubleMinValue);
-
-    }
-#endif
-}

From 6526a014fcefa4f8f2a77da39ee99166df4e8705 Mon Sep 17 00:00:00 2001
From: Artidoro Pagnoni <arpagnon@microsoft.com>
Date: Fri, 8 Feb 2019 19:03:41 -0800
Subject: [PATCH 05/23] ITransformer derives from ICanSaveModel and explicit
 implementation for ICanSaveModel (#2431)

---
 src/Microsoft.ML.Core/Data/IEstimator.cs         |  3 ++-
 .../Data}/ModelHeader.cs                         |  3 ++-
 .../Data}/ModelLoadContext.cs                    |  0
 .../Data}/ModelLoading.cs                        |  6 ++++++
 .../Data}/ModelSaveContext.cs                    |  0
 .../Data}/ModelSaving.cs                         |  0
 .../Data}/Repository.cs                          | 12 ++++++------
 .../DataLoadSave/Binary/BinaryLoader.cs          |  2 +-
 .../DataLoadSave/CompositeDataLoader.cs          |  2 +-
 .../DataLoadSave/Text/TextLoader.cs              |  6 +++---
 .../DataLoadSave/TransformWrapper.cs             |  4 ++--
 .../DataLoadSave/TransformerChain.cs             |  6 +++---
 .../DataLoadSave/Transpose/TransposeLoader.cs    |  2 +-
 .../DataView/LambdaColumnMapper.cs               |  2 +-
 src/Microsoft.ML.Data/DataView/LambdaFilter.cs   |  2 +-
 .../DataView/RowToRowMapperTransform.cs          |  2 +-
 .../Dirty/ChooseColumnsByIndexTransform.cs       |  2 +-
 .../Evaluators/BinaryClassifierEvaluator.cs      |  4 ++--
 .../Evaluators/ClusteringEvaluator.cs            |  4 ++--
 .../Evaluators/EvaluatorBase.cs                  |  7 ++++++-
 .../Evaluators/MultiClassClassifierEvaluator.cs  |  4 ++--
 .../Evaluators/MultiOutputRegressionEvaluator.cs |  4 ++--
 .../Evaluators/QuantileRegressionEvaluator.cs    |  4 ++--
 .../Evaluators/RankerEvaluator.cs                |  8 ++++----
 .../Evaluators/RegressionEvaluator.cs            |  4 ++--
 src/Microsoft.ML.Data/Prediction/Calibrator.cs   | 10 +++++-----
 .../Prediction/CalibratorCatalog.cs              |  4 ++--
 .../Scorers/FeatureContributionCalculation.cs    |  2 +-
 src/Microsoft.ML.Data/Scorers/GenericScorer.cs   |  4 ++--
 .../Scorers/MultiClassClassifierScorer.cs        |  2 +-
 .../Scorers/PredictedLabelScorerBase.cs          |  4 ++--
 .../Scorers/PredictionTransformer.cs             | 12 ++++++++----
 .../Scorers/RowToRowScorerBase.cs                |  4 ++--
 .../Scorers/SchemaBindablePredictorWrapper.cs    | 16 +++++++++-------
 .../Transforms/BootstrapSamplingTransformer.cs   |  2 +-
 .../Transforms/ColumnConcatenatingTransformer.cs |  4 ++--
 .../Transforms/ColumnCopying.cs                  |  2 +-
 .../Transforms/ColumnSelecting.cs                |  8 +++++---
 .../FeatureContributionCalculationTransformer.cs |  2 +-
 .../Transforms/GenerateNumberTransform.cs        |  4 ++--
 src/Microsoft.ML.Data/Transforms/Hashing.cs      |  2 +-
 src/Microsoft.ML.Data/Transforms/KeyToValue.cs   |  2 +-
 src/Microsoft.ML.Data/Transforms/KeyToVector.cs  |  2 +-
 .../Transforms/LabelConvertTransform.cs          |  2 +-
 .../Transforms/LabelIndicatorTransform.cs        |  2 +-
 src/Microsoft.ML.Data/Transforms/NAFilter.cs     |  2 +-
 src/Microsoft.ML.Data/Transforms/NopTransform.cs |  2 +-
 .../Transforms/NormalizeColumn.cs                | 12 +++++++++---
 .../Transforms/NormalizeColumnDbl.cs             | 12 ++++++------
 .../Transforms/NormalizeColumnSng.cs             | 12 ++++++------
 src/Microsoft.ML.Data/Transforms/Normalizer.cs   |  2 +-
 .../Transforms/OneToOneTransformerBase.cs        |  2 +-
 .../Transforms/PerGroupTransformBase.cs          |  4 +++-
 src/Microsoft.ML.Data/Transforms/RangeFilter.cs  |  2 +-
 .../Transforms/RowShufflingTransformer.cs        |  2 +-
 .../Transforms/RowToRowTransformerBase.cs        | 10 +++++++---
 .../Transforms/SkipTakeFilter.cs                 |  2 +-
 .../Transforms/SlotsDroppingTransformer.cs       |  2 +-
 .../Transforms/TransformBase.cs                  |  6 ++++--
 .../Transforms/TypeConverting.cs                 |  2 +-
 src/Microsoft.ML.Data/Transforms/ValueMapping.cs |  2 +-
 .../Transforms/ValueToKeyMappingTransformer.cs   |  2 +-
 .../OutputCombiners/Average.cs                   |  2 +-
 .../OutputCombiners/BaseAverager.cs              |  4 ++--
 .../OutputCombiners/BaseMultiCombiner.cs         |  4 ++--
 .../OutputCombiners/BaseStacking.cs              |  4 ++--
 .../OutputCombiners/Median.cs                    |  2 +-
 .../OutputCombiners/MultiAverage.cs              |  2 +-
 .../OutputCombiners/MultiMedian.cs               |  2 +-
 .../OutputCombiners/MultiStacking.cs             |  2 +-
 .../OutputCombiners/MultiVoting.cs               |  2 +-
 .../OutputCombiners/MultiWeightedAverage.cs      |  2 +-
 .../OutputCombiners/RegressionStacking.cs        |  2 +-
 .../OutputCombiners/Stacking.cs                  |  2 +-
 .../OutputCombiners/Voting.cs                    |  2 +-
 .../OutputCombiners/WeightedAverage.cs           |  2 +-
 src/Microsoft.ML.Ensemble/PipelineEnsemble.cs    |  2 +-
 .../InternalQuantileRegressionTree.cs            |  2 +-
 .../TreeEnsemble/InternalRegressionTree.cs       |  2 +-
 .../TreeEnsemble/InternalTreeEnsemble.cs         |  2 +-
 .../TreeEnsembleFeaturizer.cs                    |  2 +-
 src/Microsoft.ML.HalLearners/VectorWhitening.cs  |  2 +-
 .../ImageGrayscale.cs                            |  2 +-
 src/Microsoft.ML.ImageAnalytics/ImageLoader.cs   |  2 +-
 .../ImagePixelExtractor.cs                       |  2 +-
 src/Microsoft.ML.ImageAnalytics/ImageResizer.cs  |  2 +-
 .../VectorToImageTransform.cs                    |  4 ++--
 src/Microsoft.ML.OnnxTransform/OnnxTransform.cs  |  4 ++--
 src/Microsoft.ML.PCA/PcaTransformer.cs           |  4 ++--
 src/Microsoft.ML.Parquet/ParquetLoader.cs        |  2 +-
 .../PartitionedFileLoader.cs                     |  2 +-
 .../PartitionedPathParser.cs                     |  4 ++--
 .../MatrixFactorizationPredictor.cs              |  6 +++---
 ...ldAwareFactorizationMachineModelParameters.cs |  4 ++--
 .../Standard/ModelStatistics.cs                  |  2 +-
 .../TensorflowTransform.cs                       |  4 ++--
 .../AdaptiveSingularSpectrumSequenceModeler.cs   |  2 +-
 .../ExponentialAverageTransform.cs               |  4 ++--
 .../IidAnomalyDetectionBase.cs                   |  4 ++--
 .../IidChangePointDetector.cs                    |  4 ++--
 src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs  |  4 ++--
 .../MovingAverageTransform.cs                    |  4 ++--
 src/Microsoft.ML.TimeSeries/PValueTransform.cs   |  4 ++--
 .../PercentileThresholdTransform.cs              |  4 ++--
 .../SequenceModelerBase.cs                       |  4 +++-
 .../SequentialAnomalyDetectionTransformBase.cs   |  6 +++---
 .../SequentialTransformBase.cs                   |  2 +-
 .../SequentialTransformerBase.cs                 | 12 +++++++-----
 .../SlidingWindowTransform.cs                    |  4 ++--
 .../SlidingWindowTransformBase.cs                |  4 ++--
 .../SsaAnomalyDetectionBase.cs                   |  4 ++--
 .../SsaChangePointDetector.cs                    |  4 ++--
 src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs  |  4 ++--
 .../CustomMappingTransformer.cs                  |  9 +++++----
 .../FourierDistributionSampler.cs                |  4 ++--
 src/Microsoft.ML.Transforms/GcnTransform.cs      |  2 +-
 src/Microsoft.ML.Transforms/GroupTransform.cs    |  4 ++--
 .../HashJoiningTransform.cs                      |  2 +-
 .../KeyToVectorMapping.cs                        |  2 +-
 src/Microsoft.ML.Transforms/LambdaTransform.cs   |  4 ++--
 .../MissingValueDroppingTransformer.cs           |  2 +-
 .../MissingValueIndicatorTransform.cs            |  2 +-
 .../MissingValueIndicatorTransformer.cs          |  2 +-
 .../MissingValueReplacing.cs                     |  2 +-
 src/Microsoft.ML.Transforms/OneHotEncoding.cs    |  4 ++--
 .../OneHotHashEncoding.cs                        |  4 ++--
 .../OptionalColumnTransform.cs                   |  2 +-
 .../ProduceIdTransform.cs                        |  6 +++---
 .../RandomFourierFeaturizing.cs                  |  4 ++--
 src/Microsoft.ML.Transforms/Text/LdaTransform.cs |  4 ++--
 .../Text/NgramHashingTransformer.cs              |  4 ++--
 .../Text/NgramTransform.cs                       |  4 ++--
 .../Text/StopWordsRemovingTransformer.cs         |  6 +++---
 .../Text/TextFeaturizingEstimator.cs             |  4 ++--
 .../Text/TextNormalizing.cs                      |  2 +-
 .../Text/TokenizingByCharacters.cs               |  2 +-
 .../Text/WordEmbeddingsExtractor.cs              |  2 +-
 .../Text/WordTokenizing.cs                       |  2 +-
 src/Microsoft.ML.Transforms/UngroupTransform.cs  |  4 ++--
 .../Helpers/DiagnosticVerifier.cs                |  3 ++-
 140 files changed, 279 insertions(+), 238 deletions(-)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/ModelHeader.cs (99%)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/ModelLoadContext.cs (100%)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/ModelLoading.cs (98%)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/ModelSaveContext.cs (100%)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/ModelSaving.cs (100%)
 rename src/{Microsoft.ML.Data/Model => Microsoft.ML.Core/Data}/Repository.cs (97%)

diff --git a/src/Microsoft.ML.Core/Data/IEstimator.cs b/src/Microsoft.ML.Core/Data/IEstimator.cs
index 6ebef55827..1e96439a1e 100644
--- a/src/Microsoft.ML.Core/Data/IEstimator.cs
+++ b/src/Microsoft.ML.Core/Data/IEstimator.cs
@@ -7,6 +7,7 @@
 using System.Linq;
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
+using Microsoft.ML.Model;
 
 namespace Microsoft.ML.Core.Data
 {
@@ -263,7 +264,7 @@ public interface IDataReaderEstimator<in TSource, out TReader>
     /// The transformer is a component that transforms data.
     /// It also supports 'schema propagation' to answer the question of 'how will the data with this schema look, after you transform it?'.
     /// </summary>
-    public interface ITransformer
+    public interface ITransformer : ICanSaveModel
     {
         /// <summary>
         /// Schema propagation for transformers.
diff --git a/src/Microsoft.ML.Data/Model/ModelHeader.cs b/src/Microsoft.ML.Core/Data/ModelHeader.cs
similarity index 99%
rename from src/Microsoft.ML.Data/Model/ModelHeader.cs
rename to src/Microsoft.ML.Core/Data/ModelHeader.cs
index 88fcf9bdad..d1b572e8bc 100644
--- a/src/Microsoft.ML.Data/Model/ModelHeader.cs
+++ b/src/Microsoft.ML.Core/Data/ModelHeader.cs
@@ -10,7 +10,8 @@
 
 namespace Microsoft.ML.Model
 {
-    [StructLayout(LayoutKind.Explicit, Size = ModelHeader.Size)]
+    [BestFriend]
+    [StructLayout(LayoutKind.Explicit, Size = Size)]
     internal struct ModelHeader
     {
         /// <summary>
diff --git a/src/Microsoft.ML.Data/Model/ModelLoadContext.cs b/src/Microsoft.ML.Core/Data/ModelLoadContext.cs
similarity index 100%
rename from src/Microsoft.ML.Data/Model/ModelLoadContext.cs
rename to src/Microsoft.ML.Core/Data/ModelLoadContext.cs
diff --git a/src/Microsoft.ML.Data/Model/ModelLoading.cs b/src/Microsoft.ML.Core/Data/ModelLoading.cs
similarity index 98%
rename from src/Microsoft.ML.Data/Model/ModelLoading.cs
rename to src/Microsoft.ML.Core/Data/ModelLoading.cs
index 06ebc0bf06..d561389e39 100644
--- a/src/Microsoft.ML.Data/Model/ModelLoading.cs
+++ b/src/Microsoft.ML.Core/Data/ModelLoading.cs
@@ -10,6 +10,12 @@
 
 namespace Microsoft.ML.Model
 {
+    /// <summary>
+    /// Signature for a repository based model loader. This is the dual of <see cref="ICanSaveModel"/>.
+    /// </summary>
+    [BestFriend]
+    internal delegate void SignatureLoadModel(ModelLoadContext ctx);
+
     public sealed partial class ModelLoadContext : IDisposable
     {
         public const string ModelStreamName = "Model.key";
diff --git a/src/Microsoft.ML.Data/Model/ModelSaveContext.cs b/src/Microsoft.ML.Core/Data/ModelSaveContext.cs
similarity index 100%
rename from src/Microsoft.ML.Data/Model/ModelSaveContext.cs
rename to src/Microsoft.ML.Core/Data/ModelSaveContext.cs
diff --git a/src/Microsoft.ML.Data/Model/ModelSaving.cs b/src/Microsoft.ML.Core/Data/ModelSaving.cs
similarity index 100%
rename from src/Microsoft.ML.Data/Model/ModelSaving.cs
rename to src/Microsoft.ML.Core/Data/ModelSaving.cs
diff --git a/src/Microsoft.ML.Data/Model/Repository.cs b/src/Microsoft.ML.Core/Data/Repository.cs
similarity index 97%
rename from src/Microsoft.ML.Data/Model/Repository.cs
rename to src/Microsoft.ML.Core/Data/Repository.cs
index dd3ebfe43a..42cf955b84 100644
--- a/src/Microsoft.ML.Data/Model/Repository.cs
+++ b/src/Microsoft.ML.Core/Data/Repository.cs
@@ -10,13 +10,11 @@
 
 namespace Microsoft.ML.Model
 {
-    /// <summary>
-    /// Signature for a repository based model loader. This is the dual of ICanSaveModel.
-    /// </summary>
-    public delegate void SignatureLoadModel(ModelLoadContext ctx);
-
     /// <summary>
     /// For saving a model into a repository.
+    /// Classes implementing <see cref="ICanSaveModel"/> should do an explicit implementation of <see cref="Save(ModelSaveContext)"/>.
+    /// Classes inheriting <see cref="ICanSaveModel"/> from a base class should overwrite the function invoked by <see cref="Save(ModelSaveContext)"/>
+    /// in that base class, if there is one.
     /// </summary>
     public interface ICanSaveModel
     {
@@ -293,6 +291,8 @@ protected Entry AddEntry(string pathEnt, Stream stream)
 
     public sealed class RepositoryWriter : Repository
     {
+        private const string DirTrainingInfo = "TrainingInfo";
+
         private ZipArchive _archive;
         private Queue<KeyValuePair<string, Stream>> _closed;
 
@@ -301,7 +301,7 @@ public static RepositoryWriter CreateNew(Stream stream, IExceptionContext ectx =
             Contracts.CheckValueOrNull(ectx);
             ectx.CheckValue(stream, nameof(stream));
             var rep = new RepositoryWriter(stream, ectx, useFileSystem);
-            using (var ent = rep.CreateEntry(ModelFileUtils.DirTrainingInfo, "Version.txt"))
+            using (var ent = rep.CreateEntry(DirTrainingInfo, "Version.txt"))
             using (var writer = Utils.OpenWriter(ent.Stream))
                 writer.WriteLine(typeof(RepositoryWriter).Assembly.GetName().Version);
             return rep;
diff --git a/src/Microsoft.ML.Data/DataLoadSave/Binary/BinaryLoader.cs b/src/Microsoft.ML.Data/DataLoadSave/Binary/BinaryLoader.cs
index 25d00518f3..a42a84d559 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/Binary/BinaryLoader.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/Binary/BinaryLoader.cs
@@ -942,7 +942,7 @@ private static Stream OpenStream(string filename)
             return OpenStream(files);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/DataLoadSave/CompositeDataLoader.cs b/src/Microsoft.ML.Data/DataLoadSave/CompositeDataLoader.cs
index ff5348c58c..ccd25a0039 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/CompositeDataLoader.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/CompositeDataLoader.cs
@@ -502,7 +502,7 @@ private static IDataLoader LoadTransforms(ModelLoadContext ctx, IDataLoader srcL
                 });
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/DataLoadSave/Text/TextLoader.cs b/src/Microsoft.ML.Data/DataLoadSave/Text/TextLoader.cs
index 795fa2c66e..2a8a25df2b 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/Text/TextLoader.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/Text/TextLoader.cs
@@ -834,7 +834,7 @@ public Bindings(ModelLoadContext ctx, TextLoader parent)
                 OutputSchema = ComputeOutputSchema();
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -1283,7 +1283,7 @@ internal static IDataLoader Create(IHostEnvironment env, Arguments args, IMultiS
         internal static IDataView ReadFile(IHostEnvironment env, Arguments args, IMultiStreamSource fileSource)
             => new TextLoader(env, args, fileSource).Read(fileSource);
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -1420,7 +1420,7 @@ public RowCursor[] GetRowCursorSet(IEnumerable<Schema.Column> columnsNeeded, int
                 return Cursor.CreateSet(_reader, _files, active, n);
             }
 
-            public void Save(ModelSaveContext ctx) => _reader.Save(ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => ((ICanSaveModel)_reader).Save(ctx);
         }
     }
 }
\ No newline at end of file
diff --git a/src/Microsoft.ML.Data/DataLoadSave/TransformWrapper.cs b/src/Microsoft.ML.Data/DataLoadSave/TransformWrapper.cs
index bf2af44b4c..24a29b1cc9 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/TransformWrapper.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/TransformWrapper.cs
@@ -18,7 +18,7 @@ namespace Microsoft.ML.Data
 {
     // REVIEW: this class is public, as long as the Wrappers.cs in tests still rely on it.
     // It needs to become internal.
-    public sealed class TransformWrapper : ITransformer, ICanSaveModel
+    public sealed class TransformWrapper : ITransformer
     {
         public const string LoaderSignature = "TransformWrapper";
         private const string TransformDirTemplate = "Step_{0:000}";
@@ -46,7 +46,7 @@ public Schema GetOutputSchema(Schema inputSchema)
             return output.Schema;
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             if (!_allowSave)
                 throw _host.Except("Saving is not permitted.");
diff --git a/src/Microsoft.ML.Data/DataLoadSave/TransformerChain.cs b/src/Microsoft.ML.Data/DataLoadSave/TransformerChain.cs
index 6b5ee01d8d..6e52f83e75 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/TransformerChain.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/TransformerChain.cs
@@ -51,7 +51,7 @@ internal interface ITransformerChainAccessor
     /// A chain of transformers (possibly empty) that end with a <typeparamref name="TLastTransformer"/>.
     /// For an empty chain, <typeparamref name="TLastTransformer"/> is always <see cref="ITransformer"/>.
     /// </summary>
-    public sealed class TransformerChain<TLastTransformer> : ITransformer, ICanSaveModel, IEnumerable<ITransformer>, ITransformerChainAccessor
+    public sealed class TransformerChain<TLastTransformer> : ITransformer, IEnumerable<ITransformer>, ITransformerChainAccessor
     where TLastTransformer : class, ITransformer
     {
         private readonly ITransformer[] _transformers;
@@ -165,7 +165,7 @@ public TransformerChain<TNewLast> Append<TNewLast>(TNewLast transformer, Transfo
             return new TransformerChain<TNewLast>(_transformers.AppendElement(transformer), _scopes.AppendElement(scope));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
@@ -181,7 +181,7 @@ public void Save(ModelSaveContext ctx)
         }
 
         /// <summary>
-        /// The loading constructor of transformer chain. Reverse of <see cref="Save(ModelSaveContext)"/>.
+        /// The loading constructor of transformer chain. Reverse of <see cref="ICanSaveModel.Save"/>.
         /// </summary>
         internal TransformerChain(IHostEnvironment env, ModelLoadContext ctx)
         {
diff --git a/src/Microsoft.ML.Data/DataLoadSave/Transpose/TransposeLoader.cs b/src/Microsoft.ML.Data/DataLoadSave/Transpose/TransposeLoader.cs
index 625bcf71e7..f3b7629bc4 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/Transpose/TransposeLoader.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/Transpose/TransposeLoader.cs
@@ -513,7 +513,7 @@ public static TransposeLoader Create(IHostEnvironment env, ModelLoadContext ctx,
                 });
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/DataView/LambdaColumnMapper.cs b/src/Microsoft.ML.Data/DataView/LambdaColumnMapper.cs
index 9f7290e0d1..c34881b2d6 100644
--- a/src/Microsoft.ML.Data/DataView/LambdaColumnMapper.cs
+++ b/src/Microsoft.ML.Data/DataView/LambdaColumnMapper.cs
@@ -140,7 +140,7 @@ public Impl(IHostEnvironment env, string name, IDataView input, OneToOneColumn c
                 Metadata.Seal();
             }
 
-            public override void Save(ModelSaveContext ctx)
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
                 Host.Assert(false, "Shouldn't serialize this!");
                 throw Host.ExceptNotSupp("Shouldn't serialize this");
diff --git a/src/Microsoft.ML.Data/DataView/LambdaFilter.cs b/src/Microsoft.ML.Data/DataView/LambdaFilter.cs
index 9e726ad9d4..f1cf418431 100644
--- a/src/Microsoft.ML.Data/DataView/LambdaFilter.cs
+++ b/src/Microsoft.ML.Data/DataView/LambdaFilter.cs
@@ -96,7 +96,7 @@ public Impl(IHostEnvironment env, string name, IDataView input,
                 _conv = conv;
             }
 
-            public override void Save(ModelSaveContext ctx)
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
                 Host.Assert(false, "Shouldn't serialize this!");
                 throw Host.ExceptNotSupp("Shouldn't serialize this");
diff --git a/src/Microsoft.ML.Data/DataView/RowToRowMapperTransform.cs b/src/Microsoft.ML.Data/DataView/RowToRowMapperTransform.cs
index e3a2377009..23b0211404 100644
--- a/src/Microsoft.ML.Data/DataView/RowToRowMapperTransform.cs
+++ b/src/Microsoft.ML.Data/DataView/RowToRowMapperTransform.cs
@@ -131,7 +131,7 @@ public static RowToRowMapperTransform Create(IHostEnvironment env, ModelLoadCont
             return h.Apply("Loading Model", ch => new RowToRowMapperTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Dirty/ChooseColumnsByIndexTransform.cs b/src/Microsoft.ML.Data/Dirty/ChooseColumnsByIndexTransform.cs
index 3b88307eb6..affe071745 100644
--- a/src/Microsoft.ML.Data/Dirty/ChooseColumnsByIndexTransform.cs
+++ b/src/Microsoft.ML.Data/Dirty/ChooseColumnsByIndexTransform.cs
@@ -223,7 +223,7 @@ public static ChooseColumnsByIndexTransform Create(IHostEnvironment env, ModelLo
             return h.Apply("Loading Model", ch => new ChooseColumnsByIndexTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
index c4e5ad76cd..7bade429f1 100644
--- a/src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
@@ -948,7 +948,7 @@ public static BinaryPerInstanceEvaluator Create(IHostEnvironment env, ModelLoadC
             return new BinaryPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -960,7 +960,7 @@ public override void Save(ModelSaveContext ctx)
             // float: _threshold
             // byte: _useRaw
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.SaveStringOrNull(_probCol);
             Contracts.Assert(FloatUtils.IsFinite(_threshold));
             ctx.Writer.Write(_threshold);
diff --git a/src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
index 067dccfb50..f1c4b19b0a 100644
--- a/src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
@@ -628,13 +628,13 @@ public static ClusteringPerInstanceEvaluator Create(IHostEnvironment env, ModelL
             return new ClusteringPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             // *** Binary format **
             // base
             // int: number of clusters
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             Host.Assert(_numClusters > 0);
             ctx.Writer.Write(_numClusters);
         }
diff --git a/src/Microsoft.ML.Data/Evaluators/EvaluatorBase.cs b/src/Microsoft.ML.Data/Evaluators/EvaluatorBase.cs
index 88fa1ee285..f15c514e0e 100644
--- a/src/Microsoft.ML.Data/Evaluators/EvaluatorBase.cs
+++ b/src/Microsoft.ML.Data/Evaluators/EvaluatorBase.cs
@@ -510,7 +510,12 @@ protected PerInstanceEvaluatorBase(IHostEnvironment env, ModelLoadContext ctx,
                 throw Host.ExceptSchemaMismatch(nameof(schema), "score", ScoreCol);
         }
 
-        public virtual void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        /// <summary>
+        /// Derived class, for example A, should overwrite <see cref="SaveModel"/> so that ((<see cref="ICanSaveModel"/>)A).Save(ctx) can correctly dump A.
+        /// </summary>
+        private protected virtual void SaveModel(ModelSaveContext ctx)
         {
             // *** Binary format **
             // int: Id of the score column name
diff --git a/src/Microsoft.ML.Data/Evaluators/MultiClassClassifierEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/MultiClassClassifierEvaluator.cs
index 7e78be4a75..bb0a27d87d 100644
--- a/src/Microsoft.ML.Data/Evaluators/MultiClassClassifierEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/MultiClassClassifierEvaluator.cs
@@ -631,7 +631,7 @@ public static MultiClassPerInstanceEvaluator Create(IHostEnvironment env, ModelL
             return new MultiClassPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -642,7 +642,7 @@ public override void Save(ModelSaveContext ctx)
             // int: number of classes
             // int[]: Ids of the class names
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             Host.Assert(_numClasses > 0);
             ctx.Writer.Write(_numClasses);
             for (int i = 0; i < _numClasses; i++)
diff --git a/src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
index 1e232aff0d..6757da940c 100644
--- a/src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
@@ -426,7 +426,7 @@ public static MultiOutputRegressionPerInstanceEvaluator Create(IHostEnvironment
             return new MultiOutputRegressionPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -434,7 +434,7 @@ public override void Save(ModelSaveContext ctx)
 
             // *** Binary format **
             // base
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> activeOutput)
diff --git a/src/Microsoft.ML.Data/Evaluators/QuantileRegressionEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/QuantileRegressionEvaluator.cs
index 1332c58948..1f16938682 100644
--- a/src/Microsoft.ML.Data/Evaluators/QuantileRegressionEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/QuantileRegressionEvaluator.cs
@@ -324,7 +324,7 @@ public static QuantileRegressionPerInstanceEvaluator Create(IHostEnvironment env
             return new QuantileRegressionPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -335,7 +335,7 @@ public override void Save(ModelSaveContext ctx)
             // int: _scoreSize
             // int[]: Ids of the quantile names
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             Host.Assert(_scoreSize > 0);
             ctx.Writer.Write(_scoreSize);
             var quantiles = _quantiles.GetValues();
diff --git a/src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
index ed215e8387..b420b1ad27 100644
--- a/src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
@@ -597,11 +597,11 @@ public static RankerPerInstanceTransform Create(IHostEnvironment env, ModelLoadC
             return h.Apply("Loading Model", ch => new RankerPerInstanceTransform(h, ctx, input));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
-            _transform.Save(ctx);
+            ((ICanSaveModel)_transform).Save(ctx);
         }
 
         public long? GetRowCount()
@@ -715,7 +715,7 @@ public Transform(IHostEnvironment env, ModelLoadContext ctx, IDataView input)
                 _bindings = new Bindings(Host, input.Schema, false, LabelCol, ScoreCol, GroupCol, _truncationLevel);
             }
 
-            public override void Save(ModelSaveContext ctx)
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
                 Host.AssertValue(ctx);
 
@@ -725,7 +725,7 @@ public override void Save(ModelSaveContext ctx)
                 // int: _labelGains.Length
                 // double[]: _labelGains
 
-                base.Save(ctx);
+                base.SaveModel(ctx);
                 Host.Assert(0 < _truncationLevel && _truncationLevel < 100);
                 ctx.Writer.Write(_truncationLevel);
                 ctx.Writer.WriteDoubleArray(_labelGains);
diff --git a/src/Microsoft.ML.Data/Evaluators/RegressionEvaluator.cs b/src/Microsoft.ML.Data/Evaluators/RegressionEvaluator.cs
index 69df01da67..2015f30eea 100644
--- a/src/Microsoft.ML.Data/Evaluators/RegressionEvaluator.cs
+++ b/src/Microsoft.ML.Data/Evaluators/RegressionEvaluator.cs
@@ -234,7 +234,7 @@ public static RegressionPerInstanceEvaluator Create(IHostEnvironment env, ModelL
             return new RegressionPerInstanceEvaluator(env, ctx, schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -242,7 +242,7 @@ public override void Save(ModelSaveContext ctx)
 
             // *** Binary format **
             // base
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> activeOutput)
diff --git a/src/Microsoft.ML.Data/Prediction/Calibrator.cs b/src/Microsoft.ML.Data/Prediction/Calibrator.cs
index ef942c5cdf..2ee036d760 100644
--- a/src/Microsoft.ML.Data/Prediction/Calibrator.cs
+++ b/src/Microsoft.ML.Data/Prediction/Calibrator.cs
@@ -407,7 +407,7 @@ private static ValueMapperCalibratedModelParameters<TSubModel, TCalibrator> Crea
             return new ValueMapperCalibratedModelParameters<TSubModel, TCalibrator>(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -462,7 +462,7 @@ private static FeatureWeightsCalibratedModelParameters<TSubModel, TCalibrator> C
             return new FeatureWeightsCalibratedModelParameters<TSubModel, TCalibrator>(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -528,7 +528,7 @@ private static ParameterMixingCalibratedModelParameters<TSubModel, TCalibrator>
             return new ParameterMixingCalibratedModelParameters<TSubModel, TCalibrator>(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -698,7 +698,7 @@ private static SchemaBindableCalibratedModelParameters<TSubModel, TCalibrator> C
             return new SchemaBindableCalibratedModelParameters<TSubModel, TCalibrator>(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.AssertValue(ctx);
             ctx.CheckAtModel();
@@ -1488,7 +1488,7 @@ private static PlattCalibrator Create(IHostEnvironment env, ModelLoadContext ctx
             return new PlattCalibrator(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Prediction/CalibratorCatalog.cs b/src/Microsoft.ML.Data/Prediction/CalibratorCatalog.cs
index ca384c0e4e..f53bb6993e 100644
--- a/src/Microsoft.ML.Data/Prediction/CalibratorCatalog.cs
+++ b/src/Microsoft.ML.Data/Prediction/CalibratorCatalog.cs
@@ -200,7 +200,7 @@ internal CalibratorTransformer(IHostEnvironment env, ModelLoadContext ctx, strin
 
         bool ITransformer.IsRowToRowMapper => true;
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.AssertValue(ctx);
             ctx.CheckAtModel();
@@ -245,7 +245,7 @@ internal Mapper(CalibratorTransformer<TCalibrator> parent, TCalibrator calibrato
             private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> activeOutput)
                => col => col == _scoreColIndex;
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             protected override Schema.DetachedColumn[] GetOutputColumnsCore()
             {
diff --git a/src/Microsoft.ML.Data/Scorers/FeatureContributionCalculation.cs b/src/Microsoft.ML.Data/Scorers/FeatureContributionCalculation.cs
index afb1f9bb51..5f837507db 100644
--- a/src/Microsoft.ML.Data/Scorers/FeatureContributionCalculation.cs
+++ b/src/Microsoft.ML.Data/Scorers/FeatureContributionCalculation.cs
@@ -163,7 +163,7 @@ public ISchemaBoundMapper Bind(IHostEnvironment env, RoleMappedSchema schema)
                 return new RowMapper(env, this, schema);
             }
 
-            public void Save(ModelSaveContext ctx)
+            void ICanSaveModel.Save(ModelSaveContext ctx)
             {
                 Contracts.CheckValue(ctx, nameof(ctx));
                 ctx.SetVersionInfo(GetVersionInfo());
diff --git a/src/Microsoft.ML.Data/Scorers/GenericScorer.cs b/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
index 60f5864f9f..612231ebf8 100644
--- a/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
+++ b/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
@@ -114,7 +114,7 @@ public static Bindings Create(ModelLoadContext ctx,
                 return Create(env, bindable, input, roles, suffix, user: false);
             }
 
-            public override void Save(ModelSaveContext ctx)
+            internal override void SaveModel(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -205,7 +205,7 @@ private protected override void SaveCore(ModelSaveContext ctx)
         {
             Contracts.AssertValue(ctx);
             ctx.SetVersionInfo(GetVersionInfo());
-            _bindings.Save(ctx);
+            _bindings.SaveModel(ctx);
         }
 
         void ISaveAsPfa.SaveAsPfa(BoundPfaContext ctx)
diff --git a/src/Microsoft.ML.Data/Scorers/MultiClassClassifierScorer.cs b/src/Microsoft.ML.Data/Scorers/MultiClassClassifierScorer.cs
index 2f57bab274..25366f02f8 100644
--- a/src/Microsoft.ML.Data/Scorers/MultiClassClassifierScorer.cs
+++ b/src/Microsoft.ML.Data/Scorers/MultiClassClassifierScorer.cs
@@ -165,7 +165,7 @@ private static ISchemaBindableMapper Create(IHostEnvironment env, ModelLoadConte
                 return h.Apply("Loading Model", ch => new LabelNameBindableMapper(h, ctx));
             }
 
-            public void Save(ModelSaveContext ctx)
+            void ICanSaveModel.Save(ModelSaveContext ctx)
             {
                 Contracts.CheckValue(ctx, nameof(ctx));
                 ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Scorers/PredictedLabelScorerBase.cs b/src/Microsoft.ML.Data/Scorers/PredictedLabelScorerBase.cs
index a72b09906c..bf2ec96e10 100644
--- a/src/Microsoft.ML.Data/Scorers/PredictedLabelScorerBase.cs
+++ b/src/Microsoft.ML.Data/Scorers/PredictedLabelScorerBase.cs
@@ -160,7 +160,7 @@ public static BindingsImpl Create(ModelLoadContext ctx, Schema input,
                 return new BindingsImpl(input, rowMapper, suffix, scoreKind, false, scoreColIndex, predColType);
             }
 
-            public override void Save(ModelSaveContext ctx)
+            internal override void SaveModel(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -335,7 +335,7 @@ private protected PredictedLabelScorerBase(IHost host, ModelLoadContext ctx, IDa
         private protected override void SaveCore(ModelSaveContext ctx)
         {
             Host.AssertValue(ctx);
-            Bindings.Save(ctx);
+            Bindings.SaveModel(ctx);
         }
 
         void ISaveAsPfa.SaveAsPfa(BoundPfaContext ctx)
diff --git a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs
index cbbb2bbe19..64f71aba83 100644
--- a/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs
+++ b/src/Microsoft.ML.Data/Scorers/PredictionTransformer.cs
@@ -134,7 +134,11 @@ public IRowToRowMapper GetRowToRowMapper(Schema inputSchema)
             return (IRowToRowMapper)Scorer.ApplyToData(Host, new EmptyDataView(Host, inputSchema));
         }
 
-        protected void SaveModel(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected abstract void SaveModel(ModelSaveContext ctx);
+
+        protected void SaveModelCore(ModelSaveContext ctx)
         {
             // *** Binary format ***
             // <base info>
@@ -157,7 +161,7 @@ protected void SaveModel(ModelSaveContext ctx)
     /// Those are all the transformers that work with one feature column.
     /// </summary>
     /// <typeparam name="TModel">The model used to transform the data.</typeparam>
-    public abstract class SingleFeaturePredictionTransformerBase<TModel> : PredictionTransformerBase<TModel>, ISingleFeaturePredictionTransformer<TModel>, ICanSaveModel
+    public abstract class SingleFeaturePredictionTransformerBase<TModel> : PredictionTransformerBase<TModel>, ISingleFeaturePredictionTransformer<TModel>
         where TModel : class
     {
         /// <summary>
@@ -226,7 +230,7 @@ public sealed override Schema GetOutputSchema(Schema inputSchema)
             return Transform(new EmptyDataView(Host, inputSchema)).Schema;
         }
 
-        public void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -235,7 +239,7 @@ public void Save(ModelSaveContext ctx)
 
         protected virtual void SaveCore(ModelSaveContext ctx)
         {
-            SaveModel(ctx);
+            SaveModelCore(ctx);
             ctx.SaveStringOrNull(FeatureColumn);
         }
 
diff --git a/src/Microsoft.ML.Data/Scorers/RowToRowScorerBase.cs b/src/Microsoft.ML.Data/Scorers/RowToRowScorerBase.cs
index fb68553672..082b083a40 100644
--- a/src/Microsoft.ML.Data/Scorers/RowToRowScorerBase.cs
+++ b/src/Microsoft.ML.Data/Scorers/RowToRowScorerBase.cs
@@ -49,7 +49,7 @@ private protected RowToRowScorerBase(IHost host, ModelLoadContext ctx, IDataView
             ctx.LoadModel<ISchemaBindableMapper, SignatureLoadModel>(host, out Bindable, "SchemaBindableMapper");
         }
 
-        public sealed override void Save(ModelSaveContext ctx)
+        private protected sealed override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.AssertValue(ctx);
             ctx.CheckAtModel();
@@ -417,7 +417,7 @@ protected void SaveBase(ModelSaveContext ctx)
             }
         }
 
-        public abstract void Save(ModelSaveContext ctx);
+        internal abstract void SaveModel(ModelSaveContext ctx);
 
         protected override ColumnType GetColumnTypeCore(int iinfo)
         {
diff --git a/src/Microsoft.ML.Data/Scorers/SchemaBindablePredictorWrapper.cs b/src/Microsoft.ML.Data/Scorers/SchemaBindablePredictorWrapper.cs
index 5d5e9e3178..5b62f45a7e 100644
--- a/src/Microsoft.ML.Data/Scorers/SchemaBindablePredictorWrapper.cs
+++ b/src/Microsoft.ML.Data/Scorers/SchemaBindablePredictorWrapper.cs
@@ -76,7 +76,9 @@ protected SchemaBindablePredictorWrapperBase(IHostEnvironment env, ModelLoadCont
             ScoreType = GetScoreType(Predictor, out ValueMapper);
         }
 
-        public virtual void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected virtual void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -283,11 +285,11 @@ public static SchemaBindablePredictorWrapper Create(IHostEnvironment env, ModelL
             return new SchemaBindablePredictorWrapper(env, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         private protected override void SaveAsPfaCore(BoundPfaContext ctx, RoleMappedSchema schema, string[] outputNames)
@@ -390,11 +392,11 @@ public static SchemaBindableBinaryPredictorWrapper Create(IHostEnvironment env,
             return new SchemaBindableBinaryPredictorWrapper(env, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         private protected override void SaveAsPfaCore(BoundPfaContext ctx, RoleMappedSchema schema, string[] outputNames)
@@ -631,7 +633,7 @@ private SchemaBindableQuantileRegressionPredictor(IHostEnvironment env, ModelLoa
             Contracts.CheckDecode(Utils.Size(_quantiles) > 0);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
@@ -641,7 +643,7 @@ public override void Save(ModelSaveContext ctx)
             // int: the number of quantiles
             // double[]: the quantiles
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.WriteDoubleArray(_quantiles);
         }
 
diff --git a/src/Microsoft.ML.Data/Transforms/BootstrapSamplingTransformer.cs b/src/Microsoft.ML.Data/Transforms/BootstrapSamplingTransformer.cs
index cb61c00db1..ee3289396f 100644
--- a/src/Microsoft.ML.Data/Transforms/BootstrapSamplingTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/BootstrapSamplingTransformer.cs
@@ -129,7 +129,7 @@ private BootstrapSamplingTransformer(IHost host, ModelLoadContext ctx, IDataView
             Host.CheckDecode(_poolSize >= 0);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/ColumnConcatenatingTransformer.cs b/src/Microsoft.ML.Data/Transforms/ColumnConcatenatingTransformer.cs
index f9b5d6bf8e..8206e66723 100644
--- a/src/Microsoft.ML.Data/Transforms/ColumnConcatenatingTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/ColumnConcatenatingTransformer.cs
@@ -263,7 +263,7 @@ private static VersionInfo GetVersionInfo()
         private const int VersionAddedAliases = 0x00010002;
         private const int VersionTransformer = 0x00010003;
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -864,7 +864,7 @@ private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> a
 
             protected override Schema.DetachedColumn[] GetOutputColumnsCore() => _columns.Select(x => x.MakeSchemaColumn()).ToArray();
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             protected override Delegate MakeGetter(Row input, int iinfo, Func<int, bool> activeOutput, out Action disposer)
             {
diff --git a/src/Microsoft.ML.Data/Transforms/ColumnCopying.cs b/src/Microsoft.ML.Data/Transforms/ColumnCopying.cs
index 7dfdf17d23..07e5784bad 100644
--- a/src/Microsoft.ML.Data/Transforms/ColumnCopying.cs
+++ b/src/Microsoft.ML.Data/Transforms/ColumnCopying.cs
@@ -166,7 +166,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             ctx.SetVersionInfo(GetVersionInfo());
             SaveColumns(ctx);
diff --git a/src/Microsoft.ML.Data/Transforms/ColumnSelecting.cs b/src/Microsoft.ML.Data/Transforms/ColumnSelecting.cs
index c3e6b50018..00bd42fee0 100644
--- a/src/Microsoft.ML.Data/Transforms/ColumnSelecting.cs
+++ b/src/Microsoft.ML.Data/Transforms/ColumnSelecting.cs
@@ -124,7 +124,7 @@ public override SchemaShape GetOutputSchema(SchemaShape inputSchema)
     /// <summary>
     /// The <see cref="ColumnSelectingTransformer"/> allows the user to specify columns to drop or keep from a given input.
     /// </summary>
-    public sealed class ColumnSelectingTransformer : ITransformer, ICanSaveModel
+    public sealed class ColumnSelectingTransformer : ITransformer
     {
         internal const string Summary = "Selects which columns from the dataset to keep.";
         internal const string UserName = "Select Columns Transform";
@@ -417,7 +417,9 @@ private static IDataTransform Create(IHostEnvironment env, Options options, IDat
             return new SelectColumnsDataTransform(env, transform, new Mapper(transform, input.Schema), input);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        internal void SaveModel(ModelSaveContext ctx)
         {
             ctx.SetVersionInfo(GetVersionInfo());
 
@@ -678,7 +680,7 @@ public RowCursor[] GetRowCursorSet(IEnumerable<Schema.Column> columnsNeeded, int
                 return cursors;
             }
 
-            public void Save(ModelSaveContext ctx) => _transform.Save(ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => _transform.SaveModel(ctx);
 
             public Func<int, bool> GetDependencies(Func<int, bool> activeOutput)
             {
diff --git a/src/Microsoft.ML.Data/Transforms/FeatureContributionCalculationTransformer.cs b/src/Microsoft.ML.Data/Transforms/FeatureContributionCalculationTransformer.cs
index 461a3f61e9..f6f46d62ac 100644
--- a/src/Microsoft.ML.Data/Transforms/FeatureContributionCalculationTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/FeatureContributionCalculationTransformer.cs
@@ -172,7 +172,7 @@ private FeatureContributionCalculatingTransformer(IHostEnvironment env, ModelLoa
             Normalize = ctx.Reader.ReadBoolByte();
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
diff --git a/src/Microsoft.ML.Data/Transforms/GenerateNumberTransform.cs b/src/Microsoft.ML.Data/Transforms/GenerateNumberTransform.cs
index 54fb453894..c14cdd49d9 100644
--- a/src/Microsoft.ML.Data/Transforms/GenerateNumberTransform.cs
+++ b/src/Microsoft.ML.Data/Transforms/GenerateNumberTransform.cs
@@ -163,7 +163,7 @@ public static Bindings Create(ModelLoadContext ctx, Schema input)
                 return new Bindings(useCounter, states, input, false, names);
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -309,7 +309,7 @@ public static GenerateNumberTransform Create(IHostEnvironment env, ModelLoadCont
             return h.Apply("Loading Model", ch => new GenerateNumberTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/Hashing.cs b/src/Microsoft.ML.Data/Transforms/Hashing.cs
index f98f4baa47..d60794d2d8 100644
--- a/src/Microsoft.ML.Data/Transforms/Hashing.cs
+++ b/src/Microsoft.ML.Data/Transforms/Hashing.cs
@@ -278,7 +278,7 @@ private HashingTransformer(IHost host, ModelLoadContext ctx)
             TextModelHelper.LoadAll(Host, ctx, columnsLength, out _keyValues, out _kvTypes);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Data/Transforms/KeyToValue.cs b/src/Microsoft.ML.Data/Transforms/KeyToValue.cs
index ffb6c61da3..eaaf566e95 100644
--- a/src/Microsoft.ML.Data/Transforms/KeyToValue.cs
+++ b/src/Microsoft.ML.Data/Transforms/KeyToValue.cs
@@ -144,7 +144,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/KeyToVector.cs b/src/Microsoft.ML.Data/Transforms/KeyToVector.cs
index d2c7a5e260..cc4ed51905 100644
--- a/src/Microsoft.ML.Data/Transforms/KeyToVector.cs
+++ b/src/Microsoft.ML.Data/Transforms/KeyToVector.cs
@@ -143,7 +143,7 @@ private static VersionInfo GetVersionInfo()
                 loaderAssemblyName: typeof(KeyToVectorMappingTransformer).Assembly.FullName);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/LabelConvertTransform.cs b/src/Microsoft.ML.Data/Transforms/LabelConvertTransform.cs
index 408e62e330..cf0b9790ea 100644
--- a/src/Microsoft.ML.Data/Transforms/LabelConvertTransform.cs
+++ b/src/Microsoft.ML.Data/Transforms/LabelConvertTransform.cs
@@ -120,7 +120,7 @@ public static LabelConvertTransform Create(IHostEnvironment env, ModelLoadContex
                 });
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/LabelIndicatorTransform.cs b/src/Microsoft.ML.Data/Transforms/LabelIndicatorTransform.cs
index 21b0b13bce..e848312ee9 100644
--- a/src/Microsoft.ML.Data/Transforms/LabelIndicatorTransform.cs
+++ b/src/Microsoft.ML.Data/Transforms/LabelIndicatorTransform.cs
@@ -98,7 +98,7 @@ public static LabelIndicatorTransform Create(IHostEnvironment env,
                 ch => new LabelIndicatorTransform(h, args, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/NAFilter.cs b/src/Microsoft.ML.Data/Transforms/NAFilter.cs
index ffc04cbc80..956f201cd6 100644
--- a/src/Microsoft.ML.Data/Transforms/NAFilter.cs
+++ b/src/Microsoft.ML.Data/Transforms/NAFilter.cs
@@ -169,7 +169,7 @@ public static NAFilter Create(IHostEnvironment env, ModelLoadContext ctx, IDataV
             return h.Apply("Loading Model", ch => new NAFilter(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/NopTransform.cs b/src/Microsoft.ML.Data/Transforms/NopTransform.cs
index 92f701d704..234c060be2 100644
--- a/src/Microsoft.ML.Data/Transforms/NopTransform.cs
+++ b/src/Microsoft.ML.Data/Transforms/NopTransform.cs
@@ -89,7 +89,7 @@ private NopTransform(IHost host, ModelLoadContext ctx, IDataView input)
             // Nothing :)
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/NormalizeColumn.cs b/src/Microsoft.ML.Data/Transforms/NormalizeColumn.cs
index 4888aeb4c4..f15f3e77fc 100644
--- a/src/Microsoft.ML.Data/Transforms/NormalizeColumn.cs
+++ b/src/Microsoft.ML.Data/Transforms/NormalizeColumn.cs
@@ -369,7 +369,9 @@ private AffineColumnFunction(IHost host)
                 Host = host;
             }
 
-            public abstract void Save(ModelSaveContext ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+            private protected abstract void SaveModel(ModelSaveContext ctx);
 
             public abstract JToken PfaInfo(BoundPfaContext ctx, JToken srcToken);
             public bool CanSaveOnnx(OnnxContext ctx) => true;
@@ -485,7 +487,9 @@ private CdfColumnFunction(IHost host)
                 Host = host;
             }
 
-            public abstract void Save(ModelSaveContext ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+            private protected abstract void SaveModel(ModelSaveContext ctx);
 
             public JToken PfaInfo(BoundPfaContext ctx, JToken srcToken) => null;
 
@@ -614,7 +618,9 @@ protected BinColumnFunction(IHost host)
                 Host = host;
             }
 
-            public abstract void Save(ModelSaveContext ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+            private protected abstract void SaveModel(ModelSaveContext ctx);
 
             public JToken PfaInfo(BoundPfaContext ctx, JToken srcToken) => null;
 
diff --git a/src/Microsoft.ML.Data/Transforms/NormalizeColumnDbl.cs b/src/Microsoft.ML.Data/Transforms/NormalizeColumnDbl.cs
index 54b4c11ce4..e2050bdcdc 100644
--- a/src/Microsoft.ML.Data/Transforms/NormalizeColumnDbl.cs
+++ b/src/Microsoft.ML.Data/Transforms/NormalizeColumnDbl.cs
@@ -568,7 +568,7 @@ private void GetResult(ref TFloat input, ref TFloat value)
                         value = (input - Offset) * Scale;
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         AffineNormSerializationUtils.SaveModel(ctx, 1, null, new[] { Scale }, new[] { Offset }, saveText: true);
                     }
@@ -628,7 +628,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, scales, offsets, (offsets != null && nz.Count < cv / 2) ? nz.ToArray() : null);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         AffineNormSerializationUtils.SaveModel(ctx, Scale.Length, null, Scale, Offset, saveText: true);
                     }
@@ -892,7 +892,7 @@ private void GetResult(ref TFloat input, ref TFloat value)
                         value = CdfUtils.Cdf(val, Mean, Stddev);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -947,7 +947,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, mean, stddev, useLog);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -1071,7 +1071,7 @@ public ImplOne(IHost host, TFloat[] binUpperBounds, bool fixZero)
                         return new ImplOne(host, binUpperBounds[0], fixZero);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -1157,7 +1157,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, binUpperBounds, fixZero);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/NormalizeColumnSng.cs b/src/Microsoft.ML.Data/Transforms/NormalizeColumnSng.cs
index 482e5d8f71..7a4abcdfbb 100644
--- a/src/Microsoft.ML.Data/Transforms/NormalizeColumnSng.cs
+++ b/src/Microsoft.ML.Data/Transforms/NormalizeColumnSng.cs
@@ -570,7 +570,7 @@ private void GetResult(ref TFloat input, ref TFloat value)
                         value = (input - Offset) * Scale;
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         AffineNormSerializationUtils.SaveModel(ctx, 1, null, new[] { Scale }, new[] { Offset }, saveText: true);
                     }
@@ -629,7 +629,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, scales, offsets, (offsets != null && nz.Count < cv / 2) ? nz.ToArray() : null);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         AffineNormSerializationUtils.SaveModel(ctx, Scale.Length, null, Scale, Offset, saveText: true);
                     }
@@ -896,7 +896,7 @@ private void GetResult(ref TFloat input, ref TFloat value)
                         value = CdfUtils.Cdf(val, Mean, Stddev);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -951,7 +951,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, mean, stddev, useLog);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -1076,7 +1076,7 @@ public ImplOne(IHost host, TFloat[] binUpperBounds, bool fixZero)
                         return new ImplOne(host, binUpperBounds[0], fixZero);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
@@ -1162,7 +1162,7 @@ public static ImplVec Create(ModelLoadContext ctx, IHost host, VectorType typeSr
                         return new ImplVec(host, binUpperBounds, fixZero);
                     }
 
-                    public override void Save(ModelSaveContext ctx)
+                    private protected override void SaveModel(ModelSaveContext ctx)
                     {
                         Contracts.AssertValue(ctx);
                         ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/Normalizer.cs b/src/Microsoft.ML.Data/Transforms/Normalizer.cs
index bff9503614..78edabbe0c 100644
--- a/src/Microsoft.ML.Data/Transforms/Normalizer.cs
+++ b/src/Microsoft.ML.Data/Transforms/Normalizer.cs
@@ -540,7 +540,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/OneToOneTransformerBase.cs b/src/Microsoft.ML.Data/Transforms/OneToOneTransformerBase.cs
index f1cf1f52b3..4a4a94f7de 100644
--- a/src/Microsoft.ML.Data/Transforms/OneToOneTransformerBase.cs
+++ b/src/Microsoft.ML.Data/Transforms/OneToOneTransformerBase.cs
@@ -110,7 +110,7 @@ private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> a
                 return col => active[col];
             }
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
         }
     }
 }
diff --git a/src/Microsoft.ML.Data/Transforms/PerGroupTransformBase.cs b/src/Microsoft.ML.Data/Transforms/PerGroupTransformBase.cs
index c5e95a7683..f226d28a30 100644
--- a/src/Microsoft.ML.Data/Transforms/PerGroupTransformBase.cs
+++ b/src/Microsoft.ML.Data/Transforms/PerGroupTransformBase.cs
@@ -133,7 +133,9 @@ protected PerGroupTransformBase(IHostEnvironment env, ModelLoadContext ctx, IDat
             GroupCol = ctx.LoadNonEmptyString();
         }
 
-        public virtual void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected virtual void SaveModel(ModelSaveContext ctx)
         {
             Host.AssertValue(ctx);
 
diff --git a/src/Microsoft.ML.Data/Transforms/RangeFilter.cs b/src/Microsoft.ML.Data/Transforms/RangeFilter.cs
index 838f1698b2..e6f356ffe4 100644
--- a/src/Microsoft.ML.Data/Transforms/RangeFilter.cs
+++ b/src/Microsoft.ML.Data/Transforms/RangeFilter.cs
@@ -177,7 +177,7 @@ public static RangeFilter Create(IHostEnvironment env, ModelLoadContext ctx, IDa
             return h.Apply("Loading Model", ch => new RangeFilter(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/RowShufflingTransformer.cs b/src/Microsoft.ML.Data/Transforms/RowShufflingTransformer.cs
index 0ed952c379..0f6b6a5dc3 100644
--- a/src/Microsoft.ML.Data/Transforms/RowShufflingTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/RowShufflingTransformer.cs
@@ -159,7 +159,7 @@ public static RowShufflingTransformer Create(IHostEnvironment env, ModelLoadCont
             return h.Apply("Loading Model", ch => new RowShufflingTransformer(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/RowToRowTransformerBase.cs b/src/Microsoft.ML.Data/Transforms/RowToRowTransformerBase.cs
index 104cdad96d..13622d6063 100644
--- a/src/Microsoft.ML.Data/Transforms/RowToRowTransformerBase.cs
+++ b/src/Microsoft.ML.Data/Transforms/RowToRowTransformerBase.cs
@@ -13,7 +13,7 @@ namespace Microsoft.ML.Data
     /// <summary>
     /// Base class for transformer which produce new columns, but doesn't affect existing ones.
     /// </summary>
-    public abstract class RowToRowTransformerBase : ITransformer, ICanSaveModel
+    public abstract class RowToRowTransformerBase : ITransformer
     {
         protected readonly IHost Host;
 
@@ -23,7 +23,9 @@ protected RowToRowTransformerBase(IHost host)
             Host = host;
         }
 
-        public abstract void Save(ModelSaveContext ctx);
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected abstract void SaveModel(ModelSaveContext ctx);
 
         public bool IsRowToRowMapper => true;
 
@@ -109,7 +111,9 @@ Func<int, bool> IRowMapper.GetDependencies(Func<int, bool> activeOutput)
             [BestFriend]
             private protected abstract Func<int, bool> GetDependenciesCore(Func<int, bool> activeOutput);
 
-            public abstract void Save(ModelSaveContext ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+            private protected abstract void SaveModel(ModelSaveContext ctx);
 
             public ITransformer GetTransformer() => _parent;
         }
diff --git a/src/Microsoft.ML.Data/Transforms/SkipTakeFilter.cs b/src/Microsoft.ML.Data/Transforms/SkipTakeFilter.cs
index 6c8a316a7c..b83e701597 100644
--- a/src/Microsoft.ML.Data/Transforms/SkipTakeFilter.cs
+++ b/src/Microsoft.ML.Data/Transforms/SkipTakeFilter.cs
@@ -168,7 +168,7 @@ public static SkipTakeFilter Create(IHostEnvironment env, ModelLoadContext ctx,
         }
 
         ///<summary>Saves class data to context</summary>
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/SlotsDroppingTransformer.cs b/src/Microsoft.ML.Data/Transforms/SlotsDroppingTransformer.cs
index 9b57d6d031..037521e7e1 100644
--- a/src/Microsoft.ML.Data/Transforms/SlotsDroppingTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/SlotsDroppingTransformer.cs
@@ -324,7 +324,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/TransformBase.cs b/src/Microsoft.ML.Data/Transforms/TransformBase.cs
index ec8dec1af9..0ab2b61c27 100644
--- a/src/Microsoft.ML.Data/Transforms/TransformBase.cs
+++ b/src/Microsoft.ML.Data/Transforms/TransformBase.cs
@@ -43,7 +43,9 @@ protected TransformBase(IHost host, IDataView input)
             Source = input;
         }
 
-        public abstract void Save(ModelSaveContext ctx);
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected abstract void SaveModel(ModelSaveContext ctx);
 
         public abstract long? GetRowCount();
 
@@ -388,7 +390,7 @@ public static Bindings Create(OneToOneTransformBase parent, ModelLoadContext ctx
                 return new Bindings(parent, infos, inputSchema, false, names);
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
diff --git a/src/Microsoft.ML.Data/Transforms/TypeConverting.cs b/src/Microsoft.ML.Data/Transforms/TypeConverting.cs
index 5bdbdb0a29..7dcd1ab641 100644
--- a/src/Microsoft.ML.Data/Transforms/TypeConverting.cs
+++ b/src/Microsoft.ML.Data/Transforms/TypeConverting.cs
@@ -206,7 +206,7 @@ internal TypeConvertingTransformer(IHostEnvironment env, params TypeConvertingEs
             _columns = columns.ToArray();
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Data/Transforms/ValueMapping.cs b/src/Microsoft.ML.Data/Transforms/ValueMapping.cs
index b6469b55cc..c74650a7a0 100644
--- a/src/Microsoft.ML.Data/Transforms/ValueMapping.cs
+++ b/src/Microsoft.ML.Data/Transforms/ValueMapping.cs
@@ -740,7 +740,7 @@ protected static PrimitiveType GetPrimitiveType(Type rawType, out bool isVectorT
             return ColumnTypeExtensions.PrimitiveTypeFromKind(kind);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
diff --git a/src/Microsoft.ML.Data/Transforms/ValueToKeyMappingTransformer.cs b/src/Microsoft.ML.Data/Transforms/ValueToKeyMappingTransformer.cs
index 011d491608..dacd543f29 100644
--- a/src/Microsoft.ML.Data/Transforms/ValueToKeyMappingTransformer.cs
+++ b/src/Microsoft.ML.Data/Transforms/ValueToKeyMappingTransformer.cs
@@ -641,7 +641,7 @@ private static TermMap[] Train(IHostEnvironment env, IChannel ch, ColInfo[] info
             return termMap;
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
index 05b85b37ec..4582d4f4a9 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
@@ -12,7 +12,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    public sealed class Average : BaseAverager, ICanSaveModel, IRegressionOutputCombiner
+    public sealed class Average : BaseAverager, IRegressionOutputCombiner
     {
         public const string UserName = "Average";
         public const string LoadName = "Average";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
index 1ffbe13ec1..0b454be238 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
@@ -7,7 +7,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    public abstract class BaseAverager : IBinaryOutputCombiner
+    public abstract class BaseAverager : IBinaryOutputCombiner, ICanSaveModel
     {
         protected readonly IHost Host;
         public BaseAverager(IHostEnvironment env, string name)
@@ -30,7 +30,7 @@ protected BaseAverager(IHostEnvironment env, string name, ModelLoadContext ctx)
             Host.CheckDecode(cbFloat == sizeof(Single));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
index 5e8296862b..737cbfd649 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
@@ -11,7 +11,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    public abstract class BaseMultiCombiner : IMultiClassOutputCombiner
+    public abstract class BaseMultiCombiner : IMultiClassOutputCombiner, ICanSaveModel
     {
         protected readonly IHost Host;
 
@@ -49,7 +49,7 @@ internal BaseMultiCombiner(IHostEnvironment env, string name, ModelLoadContext c
             Normalize = ctx.Reader.ReadBoolByte();
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
index 81d0c545f9..c081feee14 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
@@ -15,7 +15,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    internal abstract class BaseStacking<TOutput> : IStackingTrainer<TOutput>
+    internal abstract class BaseStacking<TOutput> : IStackingTrainer<TOutput>, ICanSaveModel
     {
         public abstract class ArgumentsBase
         {
@@ -67,7 +67,7 @@ private protected BaseStacking(IHostEnvironment env, string name, ModelLoadConte
             CheckMeta();
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.Check(Meta != null, "Can't save an untrained Stacking combiner");
             Host.CheckValue(ctx, nameof(ctx));
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
index 3b94196eb9..5173216711 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
@@ -59,7 +59,7 @@ public static Median Create(IHostEnvironment env, ModelLoadContext ctx)
             return new Median(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
index 1ba5cdf028..be3bd7dfa2 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
@@ -16,7 +16,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    public sealed class MultiAverage : BaseMultiAverager, ICanSaveModel
+    public sealed class MultiAverage : BaseMultiAverager
     {
         public const string LoadName = "MultiAverage";
         public const string LoaderSignature = "MultiAverageCombiner";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
index 477a658355..94b0f38ae8 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
@@ -19,7 +19,7 @@ namespace Microsoft.ML.Ensemble.OutputCombiners
     /// <summary>
     /// Generic interface for combining outputs of multiple models
     /// </summary>
-    public sealed class MultiMedian : BaseMultiCombiner, ICanSaveModel
+    public sealed class MultiMedian : BaseMultiCombiner
     {
         public const string LoadName = "MultiMedian";
         public const string LoaderSignature = "MultiMedianCombiner";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
index 3ec8ea85c8..f2bb13d029 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
@@ -20,7 +20,7 @@
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
     using TVectorPredictor = IPredictorProducing<VBuffer<Single>>;
-    internal sealed class MultiStacking : BaseStacking<VBuffer<Single>>, ICanSaveModel, IMultiClassOutputCombiner
+    internal sealed class MultiStacking : BaseStacking<VBuffer<Single>>, IMultiClassOutputCombiner
     {
         public const string LoadName = "MultiStacking";
         public const string LoaderSignature = "MultiStackingCombiner";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
index a8ba932926..a222606ae1 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
@@ -17,7 +17,7 @@ namespace Microsoft.ML.Ensemble.OutputCombiners
 {
     // REVIEW: Why is MultiVoting based on BaseMultiCombiner? Normalizing the model outputs
     // is senseless, so the base adds no real functionality.
-    public sealed class MultiVoting : BaseMultiCombiner, ICanSaveModel
+    public sealed class MultiVoting : BaseMultiCombiner
     {
         public const string LoadName = "MultiVoting";
         public const string LoaderSignature = "MultiVotingCombiner";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
index deef23abcd..161487650a 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
@@ -22,7 +22,7 @@ namespace Microsoft.ML.Ensemble.OutputCombiners
     /// <summary>
     /// Generic interface for combining outputs of multiple models
     /// </summary>
-    public sealed class MultiWeightedAverage : BaseMultiAverager, IWeightedAverager, ICanSaveModel
+    public sealed class MultiWeightedAverage : BaseMultiAverager, IWeightedAverager
     {
         public const string UserName = "Multi Weighted Average";
         public const string LoadName = "MultiWeightedAverage";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
index ab57cfe9aa..239e386ebf 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
@@ -19,7 +19,7 @@ namespace Microsoft.ML.Ensemble.OutputCombiners
 {
     using TScalarPredictor = IPredictorProducing<Single>;
 
-    internal sealed class RegressionStacking : BaseScalarStacking, IRegressionOutputCombiner, ICanSaveModel
+    internal sealed class RegressionStacking : BaseScalarStacking, IRegressionOutputCombiner
     {
         public const string LoadName = "RegressionStacking";
         public const string LoaderSignature = "RegressionStacking";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
index 891963dbea..9999544c6c 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
@@ -16,7 +16,7 @@
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
     using TScalarPredictor = IPredictorProducing<Single>;
-    internal sealed class Stacking : BaseScalarStacking, IBinaryOutputCombiner, ICanSaveModel
+    internal sealed class Stacking : BaseScalarStacking, IBinaryOutputCombiner
     {
         public const string UserName = "Stacking";
         public const string LoadName = "Stacking";
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
index b18de67b18..3700782957 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
@@ -57,7 +57,7 @@ public static Voting Create(IHostEnvironment env, ModelLoadContext ctx)
             return new Voting(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
index ec1d2dcd11..e6164b1914 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
@@ -19,7 +19,7 @@
 
 namespace Microsoft.ML.Ensemble.OutputCombiners
 {
-    public sealed class WeightedAverage : BaseAverager, IWeightedAverager, ICanSaveModel
+    public sealed class WeightedAverage : BaseAverager, IWeightedAverager
     {
         public const string UserName = "Weighted Average";
         public const string LoadName = "WeightedAverage";
diff --git a/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs b/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
index 2e5da36c3a..6f22df98c5 100644
--- a/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
@@ -482,7 +482,7 @@ protected SchemaBindablePipelineEnsembleBase(IHostEnvironment env, ModelLoadCont
                 _inputCols[i] = ctx.LoadNonEmptyString();
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.AssertValue(ctx);
             ctx.SetVersionInfo(GetVersionInfo());
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
index f93111201a..453a223449 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
@@ -50,7 +50,7 @@ public InternalQuantileRegressionTree(byte[] buffer, ref int position)
             _instanceWeights = buffer.ToDoubleArray(ref position);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        internal override void Save(ModelSaveContext ctx)
         {
             // *** Binary format ***
             // double[]: Labels Distribution.
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
index 4727919022..8feb116f1d 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
@@ -409,7 +409,7 @@ protected void Save(ModelSaveContext ctx, TreeType code)
             writer.WriteDoubleArray(_previousLeafValue);
         }
 
-        public virtual void Save(ModelSaveContext ctx)
+        internal virtual void Save(ModelSaveContext ctx)
         {
             Save(ctx, TreeType.Regression);
         }
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
index 421f788ef6..3a7a49573f 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
@@ -56,7 +56,7 @@ public InternalTreeEnsemble(ModelLoadContext ctx, bool usingDefaultValues, bool
             _firstInputInitializationContent = ctx.LoadStringOrNull();
         }
 
-        public void Save(ModelSaveContext ctx)
+        internal void Save(ModelSaveContext ctx)
         {
             // *** Binary format ***
             // int: Number of trees
diff --git a/src/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs b/src/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
index e1aa26f56f..18a0cf1aec 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@@ -393,7 +393,7 @@ public TreeEnsembleFeaturizerBindableMapper(IHostEnvironment env, ModelLoadConte
             _totalLeafCount = CountLeaves(_ensemble);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.HalLearners/VectorWhitening.cs b/src/Microsoft.ML.HalLearners/VectorWhitening.cs
index f865ba4fcd..fc298c1ed1 100644
--- a/src/Microsoft.ML.HalLearners/VectorWhitening.cs
+++ b/src/Microsoft.ML.HalLearners/VectorWhitening.cs
@@ -464,7 +464,7 @@ private static void TrainModels(IHostEnvironment env, IChannel ch, float[][] col
             }
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.ImageAnalytics/ImageGrayscale.cs b/src/Microsoft.ML.ImageAnalytics/ImageGrayscale.cs
index cf5388d8da..fc9ed6c699 100644
--- a/src/Microsoft.ML.ImageAnalytics/ImageGrayscale.cs
+++ b/src/Microsoft.ML.ImageAnalytics/ImageGrayscale.cs
@@ -141,7 +141,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs b/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs
index 8829d1dd2f..dab2dc17c2 100644
--- a/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs
+++ b/src/Microsoft.ML.ImageAnalytics/ImageLoader.cs
@@ -138,7 +138,7 @@ protected override void CheckInputColumn(Schema inputSchema, int col, int srcCol
                 throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", ColumnPairs[col].inputColumnName, TextType.Instance.ToString(), inputSchema[srcCol].Type.ToString());
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractor.cs b/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractor.cs
index a34fff871c..8359c4d9a6 100644
--- a/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractor.cs
+++ b/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractor.cs
@@ -252,7 +252,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.ImageAnalytics/ImageResizer.cs b/src/Microsoft.ML.ImageAnalytics/ImageResizer.cs
index 665cfd516d..a23c0d0a93 100644
--- a/src/Microsoft.ML.ImageAnalytics/ImageResizer.cs
+++ b/src/Microsoft.ML.ImageAnalytics/ImageResizer.cs
@@ -235,7 +235,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.ImageAnalytics/VectorToImageTransform.cs b/src/Microsoft.ML.ImageAnalytics/VectorToImageTransform.cs
index cc0a5da52e..da26e1f5dc 100644
--- a/src/Microsoft.ML.ImageAnalytics/VectorToImageTransform.cs
+++ b/src/Microsoft.ML.ImageAnalytics/VectorToImageTransform.cs
@@ -195,7 +195,7 @@ public ColInfoEx(ModelLoadContext ctx)
                 Interleave = ctx.Reader.ReadBoolByte();
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -306,7 +306,7 @@ public static VectorToImageTransform Create(IHostEnvironment env, ModelLoadConte
                 });
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.OnnxTransform/OnnxTransform.cs b/src/Microsoft.ML.OnnxTransform/OnnxTransform.cs
index 30f20846c4..22bb76c019 100644
--- a/src/Microsoft.ML.OnnxTransform/OnnxTransform.cs
+++ b/src/Microsoft.ML.OnnxTransform/OnnxTransform.cs
@@ -261,7 +261,7 @@ internal OnnxTransformer(IHostEnvironment env, string[] outputColumnNames, strin
         {
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.AssertValue(ctx);
 
@@ -368,7 +368,7 @@ private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> a
                 return col => Enumerable.Range(0, _parent.Outputs.Length).Any(i => activeOutput(i)) && _inputColIndices.Any(i => i == col);
             }
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             private interface INamedOnnxValueGetter
             {
diff --git a/src/Microsoft.ML.PCA/PcaTransformer.cs b/src/Microsoft.ML.PCA/PcaTransformer.cs
index b6b134a1e0..dd17255d0a 100644
--- a/src/Microsoft.ML.PCA/PcaTransformer.cs
+++ b/src/Microsoft.ML.PCA/PcaTransformer.cs
@@ -140,7 +140,7 @@ public TransformInfo(ModelLoadContext ctx)
                 Contracts.CheckDecode(MeanProjected == null || (MeanProjected.Length == Rank && FloatUtils.IsFinite(MeanProjected)));
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -279,7 +279,7 @@ private static PrincipalComponentAnalysisTransformer Create(IHostEnvironment env
             return new PrincipalComponentAnalysisTransformer(host, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Parquet/ParquetLoader.cs b/src/Microsoft.ML.Parquet/ParquetLoader.cs
index 0f733273fb..9f5eaa97d3 100644
--- a/src/Microsoft.ML.Parquet/ParquetLoader.cs
+++ b/src/Microsoft.ML.Parquet/ParquetLoader.cs
@@ -406,7 +406,7 @@ public RowCursor[] GetRowCursorSet(IEnumerable<DataViewSchema.Column> columnsNee
             return new RowCursor[] { GetRowCursor(columnsNeeded, rand) };
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Parquet/PartitionedFileLoader.cs b/src/Microsoft.ML.Parquet/PartitionedFileLoader.cs
index 8f2c83a17f..98c83a59e2 100644
--- a/src/Microsoft.ML.Parquet/PartitionedFileLoader.cs
+++ b/src/Microsoft.ML.Parquet/PartitionedFileLoader.cs
@@ -253,7 +253,7 @@ public static PartitionedFileLoader Create(IHostEnvironment env, ModelLoadContex
                 ch => new PartitionedFileLoader(host, ctx, files));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Parquet/PartitionedPathParser.cs b/src/Microsoft.ML.Parquet/PartitionedPathParser.cs
index 06ae2b9700..70a01be64b 100644
--- a/src/Microsoft.ML.Parquet/PartitionedPathParser.cs
+++ b/src/Microsoft.ML.Parquet/PartitionedPathParser.cs
@@ -148,7 +148,7 @@ public static SimplePartitionedPathParser Create(IHostEnvironment env, ModelLoad
                 ch => new SimplePartitionedPathParser(host, ctx));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
@@ -261,7 +261,7 @@ public static ParquetPartitionedPathParser Create(IHostEnvironment env, ModelLoa
                 ch => new ParquetPartitionedPathParser(host, ctx));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.CheckValue(ctx, nameof(ctx));
             ctx.SetVersionInfo(GetVersionInfo());
diff --git a/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs b/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
index 901a535307..a0007d9108 100644
--- a/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
+++ b/src/Microsoft.ML.Recommender/MatrixFactorizationPredictor.cs
@@ -155,7 +155,7 @@ private static MatrixFactorizationModelParameters Create(IHostEnvironment env, M
         /// <summary>
         /// Save model to the given context
         /// </summary>
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
@@ -391,7 +391,7 @@ public Row GetRow(Row input, Func<int, bool> active)
     /// <summary>
     /// Trains a <see cref="MatrixFactorizationModelParameters"/>. It factorizes the training matrix into the product of two low-rank matrices.
     /// </summary>
-    public sealed class MatrixFactorizationPredictionTransformer : PredictionTransformerBase<MatrixFactorizationModelParameters>, ICanSaveModel
+    public sealed class MatrixFactorizationPredictionTransformer : PredictionTransformerBase<MatrixFactorizationModelParameters>
     {
         internal const string LoaderSignature = "MaFactPredXf";
         internal string MatrixColumnIndexColumnName { get; }
@@ -488,7 +488,7 @@ public override Schema GetOutputSchema(Schema inputSchema)
             return Transform(new EmptyDataView(Host, inputSchema)).Schema;
         }
 
-        public void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachineModelParameters.cs b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachineModelParameters.cs
index 4bf005b25a..9aac84dae1 100644
--- a/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachineModelParameters.cs
+++ b/src/Microsoft.ML.StandardLearners/FactorizationMachine/FieldAwareFactorizationMachineModelParameters.cs
@@ -283,7 +283,7 @@ public float[] GetLatentWeights()
         }
     }
 
-    public sealed class FieldAwareFactorizationMachinePredictionTransformer : PredictionTransformerBase<FieldAwareFactorizationMachineModelParameters>, ICanSaveModel
+    public sealed class FieldAwareFactorizationMachinePredictionTransformer : PredictionTransformerBase<FieldAwareFactorizationMachineModelParameters>
     {
         public const string LoaderSignature = "FAFMPredXfer";
 
@@ -387,7 +387,7 @@ public override Schema GetOutputSchema(Schema inputSchema)
         /// Saves the transformer to file.
         /// </summary>
         /// <param name="ctx">The <see cref="ModelSaveContext"/> that facilitates saving to the <see cref="Repository"/>.</param>
-        public void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs b/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
index d2efdc8d57..b5cb2813af 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
@@ -166,7 +166,7 @@ internal static LinearModelStatistics Create(IHostEnvironment env, ModelLoadCont
             return new LinearModelStatistics(env, ctx);
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Contracts.AssertValue(_env);
             _env.CheckValue(ctx, nameof(ctx));
diff --git a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
index bef2bf1b1f..f87c2da37c 100644
--- a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
+++ b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
@@ -709,7 +709,7 @@ internal static (TFDataType[] tfOutputTypes, ColumnType[] outputTypes) GetOutput
 
         private protected override IRowMapper MakeRowMapper(Schema inputSchema) => new Mapper(this, inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.AssertValue(ctx);
             ctx.CheckAtModel();
@@ -877,7 +877,7 @@ public Mapper(TensorFlowTransformer parent, Schema inputSchema) :
                 }
             }
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             private class OutputCache
             {
diff --git a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
index 4a3593ca14..5a5f40a0b8 100644
--- a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
+++ b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
@@ -465,7 +465,7 @@ public AdaptiveSingularSpectrumSequenceModeler(IHostEnvironment env, ModelLoadCo
             _xSmooth = new CpuAlignedVector(_windowSize, CpuMathUtils.GetVectorAlignment());
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             _host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
index 15e7a84a8f..a7a183cd63 100644
--- a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
@@ -77,7 +77,7 @@ public ExponentialAverageTransform(IHostEnvironment env, ModelLoadContext ctx, I
             Host.CheckDecode(WindowSize == 1);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(WindowSize >= 1);
@@ -89,7 +89,7 @@ public override void Save(ModelSaveContext ctx)
             // <base>
             // Single _decay
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(_decay);
         }
 
diff --git a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
index eeff5db3d2..f552ed2860 100644
--- a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
@@ -48,11 +48,11 @@ public override Schema GetOutputSchema(Schema inputSchema)
             return Transform(new EmptyDataView(Host, inputSchema)).Schema;
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             ctx.CheckAtModel();
             Host.Assert(InitialWindowSize == 0);
-            base.Save(ctx);
+            base.SaveModel(ctx);
 
             // *** Binary format ***
             // <base>
diff --git a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
index cb0874e2ce..873b93a643 100644
--- a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
@@ -175,7 +175,7 @@ private IidChangePointDetector(IHostEnvironment env, IidChangePointDetector tran
         {
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -187,7 +187,7 @@ public override void Save(ModelSaveContext ctx)
             // *** Binary format ***
             // <base>
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
diff --git a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
index 20cecfc285..a0930d110c 100644
--- a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
@@ -155,7 +155,7 @@ private IidSpikeDetector(IHostEnvironment env, IidSpikeDetector transform)
         {
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -166,7 +166,7 @@ public override void Save(ModelSaveContext ctx)
             // *** Binary format ***
             // <base>
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
diff --git a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
index f207c7b75a..8cea2e751f 100644
--- a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
@@ -94,7 +94,7 @@ public MovingAverageTransform(IHostEnvironment env, ModelLoadContext ctx, IDataV
             Host.CheckDecode(_weights == null || Utils.Size(_weights) == WindowSize + 1 - _lag);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(WindowSize >= 1);
@@ -107,7 +107,7 @@ public override void Save(ModelSaveContext ctx)
             // int: _lag
             // Single[]: _weights
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(_lag);
             Host.Assert(_weights == null || Utils.Size(_weights) == WindowSize + 1 - _lag);
             ctx.Writer.WriteSingleArray(_weights);
diff --git a/src/Microsoft.ML.TimeSeries/PValueTransform.cs b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
index d8f3adcb29..b22d7fcc0c 100644
--- a/src/Microsoft.ML.TimeSeries/PValueTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
@@ -91,7 +91,7 @@ public PValueTransform(IHostEnvironment env, ModelLoadContext ctx, IDataView inp
             Host.CheckDecode(WindowSize >= 1);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(WindowSize >= 1);
@@ -103,7 +103,7 @@ public override void Save(ModelSaveContext ctx)
             // int: _percentile
             // byte: _isPositiveSide
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(_seed);
             ctx.Writer.WriteBoolByte(_isPositiveSide);
         }
diff --git a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
index 868e446899..18a12ed3be 100644
--- a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
@@ -86,7 +86,7 @@ public PercentileThresholdTransform(IHostEnvironment env, ModelLoadContext ctx,
             Host.CheckDecode(MinPercentile <= _percentile && _percentile <= MaxPercentile);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(MinPercentile <= _percentile && _percentile <= MaxPercentile);
@@ -98,7 +98,7 @@ public override void Save(ModelSaveContext ctx)
             // <base>
             // Double: _percentile
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(_percentile);
         }
 
diff --git a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
index d66b58c9f8..ca113b9007 100644
--- a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
@@ -75,6 +75,8 @@ private protected SequenceModelerBase()
         /// <summary>
         /// Implementation of <see cref="ICanSaveModel.Save(ModelSaveContext)"/>.
         /// </summary>
-        public abstract void Save(ModelSaveContext ctx);
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected abstract void SaveModel(ModelSaveContext ctx);
     }
 }
diff --git a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
index 91a554b96d..e0d722a7a6 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
@@ -230,7 +230,7 @@ private protected SequentialAnomalyDetectionTransformBase(IHostEnvironment env,
             _outputLength = GetOutputLength(ThresholdScore, Host);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -252,7 +252,7 @@ public override void Save(ModelSaveContext ctx)
             // Double: _powerMartingaleEpsilon
             // Double: _alertThreshold
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write((byte)Martingale);
             ctx.Writer.Write((byte)ThresholdScore);
             ctx.Writer.Write((byte)Side);
@@ -623,7 +623,7 @@ public Func<int, bool> GetDependencies(Func<int, bool> activeOutput)
                     return col => false;
             }
 
-            public void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             public Delegate[] CreateGetters(Row input, Func<int, bool> activeOutput, out Action disposer)
             {
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
index 79e2c81846..a13baf46ae 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
@@ -302,7 +302,7 @@ private protected SequentialTransformBase(IHostEnvironment env, ModelLoadContext
             _transform = CreateLambdaTransform(Host, input, OutputColumnName, InputColumnName, InitFunction, WindowSize > 0, ct);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(InitialWindowSize >= 0);
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
index dd25afe637..31a0b57ea4 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
@@ -25,7 +25,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// <typeparam name="TInput">The input type of the sequential processing.</typeparam>
     /// <typeparam name="TOutput">The dst type of the sequential processing.</typeparam>
     /// <typeparam name="TState">The state type of the sequential processing. Must be a class inherited from StateBase </typeparam>
-    public abstract class SequentialTransformerBase<TInput, TOutput, TState> : IStatefulTransformer, ICanSaveModel
+    public abstract class SequentialTransformerBase<TInput, TOutput, TState> : IStatefulTransformer
        where TState : SequentialTransformerBase<TInput, TOutput, TState>.StateBase, new()
     {
         /// <summary>
@@ -320,7 +320,9 @@ private protected SequentialTransformerBase(IHost host, ModelLoadContext ctx)
             OutputColumnType = bs.LoadTypeDescriptionOrNull(ctx.Reader.BaseStream);
         }
 
-        public virtual void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+
+        private protected virtual void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(InitialWindowSize >= 0);
@@ -450,9 +452,9 @@ protected override RowCursor GetRowCursorCore(IEnumerable<Schema.Column> columns
             public override RowCursor[] GetRowCursorSet(IEnumerable<Schema.Column> columnsNeeded, int n, Random rand = null)
                 => new RowCursor[] { GetRowCursorCore(columnsNeeded, rand) };
 
-            public override void Save(ModelSaveContext ctx)
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
-                _parent.Save(ctx);
+                (_parent as ICanSaveModel).Save(ctx);
             }
 
             IDataTransform ITransformTemplate.ApplyToData(IHostEnvironment env, IDataView newSource)
@@ -637,7 +639,7 @@ public static TimeSeriesRowToRowMapperTransform Create(IHostEnvironment env, Mod
             return h.Apply("Loading Model", ch => new TimeSeriesRowToRowMapperTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
index 96cddd0207..ca94f2c788 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
@@ -49,7 +49,7 @@ public SlidingWindowTransform(IHostEnvironment env, ModelLoadContext ctx, IDataV
             // <base>
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -57,7 +57,7 @@ public override void Save(ModelSaveContext ctx)
 
             // *** Binary format ***
             // <base>
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
     }
 }
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
index 1555c98afa..05b01a0909 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
@@ -110,7 +110,7 @@ private TInput GetNaValue()
             return nanValue;
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Assert(WindowSize >= 1);
@@ -123,7 +123,7 @@ public override void Save(ModelSaveContext ctx)
             // Int32 lag
             // byte begin
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(_lag);
             ctx.Writer.Write((byte)_begin);
         }
diff --git a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
index eac9a8ebeb..857b21bcdf 100644
--- a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
@@ -176,7 +176,7 @@ public override Schema GetOutputSchema(Schema inputSchema)
             return Transform(new EmptyDataView(Host, inputSchema)).Schema;
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -196,7 +196,7 @@ public override void Save(ModelSaveContext ctx)
             // State: StateRef
             // AdaptiveSingularSpectrumSequenceModeler: _model
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
             ctx.Writer.Write(SeasonalWindowSize);
             ctx.Writer.Write(DiscountFactor);
             ctx.Writer.Write((byte)ErrorFunction);
diff --git a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
index 3f5e3cb009..a90029b4a6 100644
--- a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
@@ -183,7 +183,7 @@ internal SsaChangePointDetector(IHostEnvironment env, ModelLoadContext ctx)
             Host.CheckDecode(IsAdaptive == false);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -197,7 +197,7 @@ public override void Save(ModelSaveContext ctx)
             // *** Binary format ***
             // <base>
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
diff --git a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
index 02b81458b3..5c1af6ccd8 100644
--- a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
@@ -165,7 +165,7 @@ internal SsaSpikeDetector(IHostEnvironment env, ModelLoadContext ctx)
             Host.CheckDecode(IsAdaptive == false);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -178,7 +178,7 @@ public override void Save(ModelSaveContext ctx)
             // *** Binary format ***
             // <base>
 
-            base.Save(ctx);
+            base.SaveModel(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
diff --git a/src/Microsoft.ML.Transforms/CustomMappingTransformer.cs b/src/Microsoft.ML.Transforms/CustomMappingTransformer.cs
index e639ad7a6d..609d9c842f 100644
--- a/src/Microsoft.ML.Transforms/CustomMappingTransformer.cs
+++ b/src/Microsoft.ML.Transforms/CustomMappingTransformer.cs
@@ -19,7 +19,7 @@ namespace Microsoft.ML.Transforms
     /// </summary>
     /// <typeparam name="TSrc">The type that describes what 'source' columns are consumed from the input <see cref="IDataView"/>.</typeparam>
     /// <typeparam name="TDst">The type that describes what new columns are added by this transform.</typeparam>
-    public sealed class CustomMappingTransformer<TSrc, TDst> : ITransformer, ICanSaveModel
+    public sealed class CustomMappingTransformer<TSrc, TDst> : ITransformer
         where TSrc : class, new()
         where TDst : class, new()
     {
@@ -60,7 +60,8 @@ public CustomMappingTransformer(IHostEnvironment env, Action<TSrc, TDst> mapActi
             AddedSchema = outSchema;
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx) => SaveModel(ctx);
+        internal void SaveModel(ModelSaveContext ctx)
         {
             if (_contractName == null)
                 throw _host.Except("Empty contract name for a transform: the transform cannot be saved");
@@ -174,8 +175,8 @@ Schema.DetachedColumn[] IRowMapper.GetOutputColumns()
                 return Enumerable.Range(0, dstRow.Schema.Count).Select(x => new Schema.DetachedColumn(dstRow.Schema[x])).ToArray();
             }
 
-            public void Save(ModelSaveContext ctx)
-                => _parent.Save(ctx);
+            void ICanSaveModel.Save(ModelSaveContext ctx)
+                => _parent.SaveModel(ctx);
 
             public ITransformer GetTransformer()
             {
diff --git a/src/Microsoft.ML.Transforms/FourierDistributionSampler.cs b/src/Microsoft.ML.Transforms/FourierDistributionSampler.cs
index af04cd10d5..40915c484c 100644
--- a/src/Microsoft.ML.Transforms/FourierDistributionSampler.cs
+++ b/src/Microsoft.ML.Transforms/FourierDistributionSampler.cs
@@ -104,7 +104,7 @@ private GaussianFourierSampler(IHostEnvironment env, ModelLoadContext ctx)
             _host.CheckDecode(FloatUtils.IsFinite(_gamma));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             ctx.SetVersionInfo(GetVersionInfo());
 
@@ -185,7 +185,7 @@ private LaplacianFourierSampler(IHostEnvironment env, ModelLoadContext ctx)
             _host.CheckDecode(FloatUtils.IsFinite(_a));
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             ctx.SetVersionInfo(GetVersionInfo());
 
diff --git a/src/Microsoft.ML.Transforms/GcnTransform.cs b/src/Microsoft.ML.Transforms/GcnTransform.cs
index f48a4329b7..5c54d5eccc 100644
--- a/src/Microsoft.ML.Transforms/GcnTransform.cs
+++ b/src/Microsoft.ML.Transforms/GcnTransform.cs
@@ -306,7 +306,7 @@ private LpNormalizingTransformer(IHost host, ModelLoadContext ctx)
                 _columns[i] = new ColumnInfoLoaded(ctx, ColumnPairs[i].outputColumnName, ColumnPairs[i].inputColumnName, ctx.Header.ModelVerWritten >= VerVectorNormalizerSupported);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Transforms/GroupTransform.cs b/src/Microsoft.ML.Transforms/GroupTransform.cs
index e36a2c4c4a..4c4ac807da 100644
--- a/src/Microsoft.ML.Transforms/GroupTransform.cs
+++ b/src/Microsoft.ML.Transforms/GroupTransform.cs
@@ -137,7 +137,7 @@ private GroupTransform(IHost host, ModelLoadContext ctx, IDataView input)
             _groupBinding = new GroupBinding(input.Schema, host, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -305,7 +305,7 @@ private Schema BuildOutputSchema(Schema sourceSchema)
                 return schemaBuilder.GetSchema();
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 _ectx.AssertValue(ctx);
 
diff --git a/src/Microsoft.ML.Transforms/HashJoiningTransform.cs b/src/Microsoft.ML.Transforms/HashJoiningTransform.cs
index b0a4edbea8..61f2e86adb 100644
--- a/src/Microsoft.ML.Transforms/HashJoiningTransform.cs
+++ b/src/Microsoft.ML.Transforms/HashJoiningTransform.cs
@@ -285,7 +285,7 @@ public static HashJoiningTransform Create(IHostEnvironment env, ModelLoadContext
             return h.Apply("Loading Model", ch => new HashJoiningTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/KeyToVectorMapping.cs b/src/Microsoft.ML.Transforms/KeyToVectorMapping.cs
index 4364ba6097..036287d3ed 100644
--- a/src/Microsoft.ML.Transforms/KeyToVectorMapping.cs
+++ b/src/Microsoft.ML.Transforms/KeyToVectorMapping.cs
@@ -83,7 +83,7 @@ internal KeyToBinaryVectorMappingTransformer(IHostEnvironment env, params (strin
         {
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Transforms/LambdaTransform.cs b/src/Microsoft.ML.Transforms/LambdaTransform.cs
index 2907a41941..c823ae3999 100644
--- a/src/Microsoft.ML.Transforms/LambdaTransform.cs
+++ b/src/Microsoft.ML.Transforms/LambdaTransform.cs
@@ -204,7 +204,7 @@ public static ITransformTemplate CreateFilter<TSrc, TState>(IHostEnvironment env
     ///  * a custom save action that serializes the transform 'state' to the binary writer.
     ///  * a custom load action that de-serializes the transform from the binary reader. This must be a public static method of a public class.
     /// </summary>
-    internal abstract class LambdaTransformBase
+    internal abstract class LambdaTransformBase : ICanSaveModel
     {
         private readonly Action<BinaryWriter> _saveAction;
         private readonly byte[] _loadFuncBytes;
@@ -248,7 +248,7 @@ protected LambdaTransformBase(IHostEnvironment env, string name, LambdaTransform
             AssertConsistentSerializable();
         }
 
-        public void Save(ModelSaveContext ctx)
+        void ICanSaveModel.Save(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             Host.Check(CanSave(), "Cannot save this transform as it was not specified as being savable");
diff --git a/src/Microsoft.ML.Transforms/MissingValueDroppingTransformer.cs b/src/Microsoft.ML.Transforms/MissingValueDroppingTransformer.cs
index a5813aa392..84593a7805 100644
--- a/src/Microsoft.ML.Transforms/MissingValueDroppingTransformer.cs
+++ b/src/Microsoft.ML.Transforms/MissingValueDroppingTransformer.cs
@@ -135,7 +135,7 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Sch
         /// <summary>
         /// Saves the transform.
         /// </summary>
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/MissingValueIndicatorTransform.cs b/src/Microsoft.ML.Transforms/MissingValueIndicatorTransform.cs
index 4cfbddf91d..74e54e896e 100644
--- a/src/Microsoft.ML.Transforms/MissingValueIndicatorTransform.cs
+++ b/src/Microsoft.ML.Transforms/MissingValueIndicatorTransform.cs
@@ -116,7 +116,7 @@ public static MissingValueIndicatorTransform Create(IHostEnvironment env, ModelL
                 });
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/MissingValueIndicatorTransformer.cs b/src/Microsoft.ML.Transforms/MissingValueIndicatorTransformer.cs
index eb91e3eeed..31d775dc68 100644
--- a/src/Microsoft.ML.Transforms/MissingValueIndicatorTransformer.cs
+++ b/src/Microsoft.ML.Transforms/MissingValueIndicatorTransformer.cs
@@ -131,7 +131,7 @@ internal static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Sc
         /// <summary>
         /// Saves the transform.
         /// </summary>
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/MissingValueReplacing.cs b/src/Microsoft.ML.Transforms/MissingValueReplacing.cs
index b99676cbc4..21c0054fff 100644
--- a/src/Microsoft.ML.Transforms/MissingValueReplacing.cs
+++ b/src/Microsoft.ML.Transforms/MissingValueReplacing.cs
@@ -492,7 +492,7 @@ private void WriteTypeAndValue<T>(Stream stream, BinarySaver saver, ColumnType t
                 throw Host.Except("We do not know how to serialize terms of type '{0}'", type);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Transforms/OneHotEncoding.cs b/src/Microsoft.ML.Transforms/OneHotEncoding.cs
index 31505972ed..bc9bec1555 100644
--- a/src/Microsoft.ML.Transforms/OneHotEncoding.cs
+++ b/src/Microsoft.ML.Transforms/OneHotEncoding.cs
@@ -26,7 +26,7 @@
 namespace Microsoft.ML.Transforms.Categorical
 {
     /// <include file='doc.xml' path='doc/members/member[@name="CategoricalOneHotVectorizer"]/*' />
-    public sealed class OneHotEncodingTransformer : ITransformer, ICanSaveModel
+    public sealed class OneHotEncodingTransformer : ITransformer
     {
         public enum OutputKind : byte
         {
@@ -165,7 +165,7 @@ internal OneHotEncodingTransformer(ValueToKeyMappingEstimator term, IEstimator<I
 
         public IDataView Transform(IDataView input) => _transformer.Transform(input);
 
-        public void Save(ModelSaveContext ctx) => _transformer.Save(ctx);
+        void ICanSaveModel.Save(ModelSaveContext ctx) => (_transformer as ICanSaveModel).Save(ctx);
 
         public bool IsRowToRowMapper => _transformer.IsRowToRowMapper;
 
diff --git a/src/Microsoft.ML.Transforms/OneHotHashEncoding.cs b/src/Microsoft.ML.Transforms/OneHotHashEncoding.cs
index 6c61b29762..df693a9c1b 100644
--- a/src/Microsoft.ML.Transforms/OneHotHashEncoding.cs
+++ b/src/Microsoft.ML.Transforms/OneHotHashEncoding.cs
@@ -24,7 +24,7 @@ namespace Microsoft.ML.Transforms.Categorical
     /// <summary>
     /// Produces a column of indicator vectors. The mapping between a value and a corresponding index is done through hashing.
     /// </summary>
-    public sealed class OneHotHashEncodingTransformer : ITransformer, ICanSaveModel
+    public sealed class OneHotHashEncodingTransformer : ITransformer
     {
         internal sealed class Column : OneToOneColumn
         {
@@ -189,7 +189,7 @@ internal OneHotHashEncodingTransformer(HashingEstimator hash, IEstimator<ITransf
         /// </summary>
         public IDataView Transform(IDataView input) => _transformer.Transform(input);
 
-        public void Save(ModelSaveContext ctx) => _transformer.Save(ctx);
+        void ICanSaveModel.Save(ModelSaveContext ctx) => (_transformer as ICanSaveModel).Save(ctx);
 
         /// <summary>
         /// Whether a call to <see cref="GetRowToRowMapper"/> should succeed, on an appropriate schema.
diff --git a/src/Microsoft.ML.Transforms/OptionalColumnTransform.cs b/src/Microsoft.ML.Transforms/OptionalColumnTransform.cs
index 3927ae6daf..c0da05f872 100644
--- a/src/Microsoft.ML.Transforms/OptionalColumnTransform.cs
+++ b/src/Microsoft.ML.Transforms/OptionalColumnTransform.cs
@@ -280,7 +280,7 @@ public static OptionalColumnTransform Create(IHostEnvironment env, ModelLoadCont
             return h.Apply("Loading Model", ch => new OptionalColumnTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/ProduceIdTransform.cs b/src/Microsoft.ML.Transforms/ProduceIdTransform.cs
index 63abdc723f..91ac258ace 100644
--- a/src/Microsoft.ML.Transforms/ProduceIdTransform.cs
+++ b/src/Microsoft.ML.Transforms/ProduceIdTransform.cs
@@ -27,7 +27,7 @@ namespace Microsoft.ML.Transforms
     /// some other file, then apply this transform to that dataview, it may of course have a different
     /// result. This is distinct from most transforms that produce results based on data alone.
     /// </summary>
-    public sealed class ProduceIdTransform : RowToRowTransformBase
+    internal sealed class ProduceIdTransform : RowToRowTransformBase
     {
         public sealed class Arguments
         {
@@ -60,7 +60,7 @@ public static Bindings Create(ModelLoadContext ctx, Schema input)
                 return new Bindings(input, true, name);
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -127,7 +127,7 @@ public static ProduceIdTransform Create(IHostEnvironment env, ModelLoadContext c
             return h.Apply("Loading Model", ch => new ProduceIdTransform(h, ctx, input));
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/RandomFourierFeaturizing.cs b/src/Microsoft.ML.Transforms/RandomFourierFeaturizing.cs
index 754ac9b573..171e8a6427 100644
--- a/src/Microsoft.ML.Transforms/RandomFourierFeaturizing.cs
+++ b/src/Microsoft.ML.Transforms/RandomFourierFeaturizing.cs
@@ -162,7 +162,7 @@ public TransformInfo(IHostEnvironment env, ModelLoadContext ctx, string director
                 InitializeFourierCoefficients(roundedUpNumFeatures, roundedUpD);
             }
 
-            public void Save(ModelSaveContext ctx, string directoryName)
+            internal void Save(ModelSaveContext ctx, string directoryName)
             {
                 Contracts.AssertValue(ctx);
 
@@ -463,7 +463,7 @@ private static RandomFourierFeaturizingTransformer Create(IHostEnvironment env,
             return new RandomFourierFeaturizingTransformer(host, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
 
diff --git a/src/Microsoft.ML.Transforms/Text/LdaTransform.cs b/src/Microsoft.ML.Transforms/Text/LdaTransform.cs
index 7141d697ec..f759edd697 100644
--- a/src/Microsoft.ML.Transforms/Text/LdaTransform.cs
+++ b/src/Microsoft.ML.Transforms/Text/LdaTransform.cs
@@ -340,7 +340,7 @@ internal LdaSummary GetLdaSummary(VBuffer<ReadOnlyMemory<char>> mapping)
                 }
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
                 long memBlockSize = 0;
@@ -733,7 +733,7 @@ private static LatentDirichletAllocationTransformer Create(IHostEnvironment env,
                 });
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/NgramHashingTransformer.cs b/src/Microsoft.ML.Transforms/Text/NgramHashingTransformer.cs
index 7d01048a44..84e95b54d4 100644
--- a/src/Microsoft.ML.Transforms/Text/NgramHashingTransformer.cs
+++ b/src/Microsoft.ML.Transforms/Text/NgramHashingTransformer.cs
@@ -256,7 +256,7 @@ internal NgramHashingTransformer(IHostEnvironment env, IDataView input, params N
             }
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -605,7 +605,7 @@ private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> a
                 return col => active[col];
             }
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
             protected override Schema.DetachedColumn[] GetOutputColumnsCore()
             {
diff --git a/src/Microsoft.ML.Transforms/Text/NgramTransform.cs b/src/Microsoft.ML.Transforms/Text/NgramTransform.cs
index b32a96aea9..acb31ed104 100644
--- a/src/Microsoft.ML.Transforms/Text/NgramTransform.cs
+++ b/src/Microsoft.ML.Transforms/Text/NgramTransform.cs
@@ -162,7 +162,7 @@ public TransformInfo(ModelLoadContext ctx, bool readWeighting)
                 NonEmptyLevels = ctx.Reader.ReadBoolArray(NgramLength);
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 Contracts.AssertValue(ctx);
 
@@ -451,7 +451,7 @@ private static NgramExtractingTransformer Create(IHostEnvironment env, ModelLoad
             return new NgramExtractingTransformer(host, ctx);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/StopWordsRemovingTransformer.cs b/src/Microsoft.ML.Transforms/Text/StopWordsRemovingTransformer.cs
index f633c711b4..46a2b56a78 100644
--- a/src/Microsoft.ML.Transforms/Text/StopWordsRemovingTransformer.cs
+++ b/src/Microsoft.ML.Transforms/Text/StopWordsRemovingTransformer.cs
@@ -203,7 +203,7 @@ internal StopWordsRemovingTransformer(IHostEnvironment env, params StopWordsRemo
             _columns = columns;
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -477,7 +477,7 @@ private protected override Func<int, bool> GetDependenciesCore(Func<int, bool> a
                 return col => active[col];
             }
 
-            public override void Save(ModelSaveContext ctx) => _parent.Save(ctx);
+            private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
         }
     }
 
@@ -850,7 +850,7 @@ internal CustomStopWordsRemovingTransformer(IHostEnvironment env, string stopwor
             LoadStopWords(ch, stopwords.AsMemory(), dataFile, stopwordsColumn, loader, out _stopWordsMap);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/TextFeaturizingEstimator.cs b/src/Microsoft.ML.Transforms/Text/TextFeaturizingEstimator.cs
index 985c6eb6fa..2ea6f6c071 100644
--- a/src/Microsoft.ML.Transforms/Text/TextFeaturizingEstimator.cs
+++ b/src/Microsoft.ML.Transforms/Text/TextFeaturizingEstimator.cs
@@ -562,7 +562,7 @@ internal static IDataTransform Create(IHostEnvironment env, Arguments args, IDat
             return estimator.Fit(data).Transform(data) as IDataTransform;
         }
 
-        private sealed class Transformer : ITransformer, ICanSaveModel
+        private sealed class Transformer : ITransformer
         {
             private const string TransformDirTemplate = "Step_{0:000}";
 
@@ -607,7 +607,7 @@ public IRowToRowMapper GetRowToRowMapper(Schema inputSchema)
                 return new CompositeRowToRowMapper(inputSchema, revMaps.ToArray());
             }
 
-            public void Save(ModelSaveContext ctx)
+            void ICanSaveModel.Save(ModelSaveContext ctx)
             {
                 _host.CheckValue(ctx, nameof(ctx));
                 ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/TextNormalizing.cs b/src/Microsoft.ML.Transforms/Text/TextNormalizing.cs
index 75bab0f8b4..e5764fafd8 100644
--- a/src/Microsoft.ML.Transforms/Text/TextNormalizing.cs
+++ b/src/Microsoft.ML.Transforms/Text/TextNormalizing.cs
@@ -118,7 +118,7 @@ protected override void CheckInputColumn(Schema inputSchema, int col, int srcCol
                 throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", ColumnPairs[col].inputColumnName, TextNormalizingEstimator.ExpectedColumnType, type.ToString());
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/TokenizingByCharacters.cs b/src/Microsoft.ML.Transforms/Text/TokenizingByCharacters.cs
index 8ef8109c52..bd23d4396e 100644
--- a/src/Microsoft.ML.Transforms/Text/TokenizingByCharacters.cs
+++ b/src/Microsoft.ML.Transforms/Text/TokenizingByCharacters.cs
@@ -136,7 +136,7 @@ private TokenizingByCharactersTransformer(IHost host, ModelLoadContext ctx) :
         private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx, IDataView input)
             => Create(env, ctx).MakeDataTransform(input);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/WordEmbeddingsExtractor.cs b/src/Microsoft.ML.Transforms/Text/WordEmbeddingsExtractor.cs
index 8a6c8108b5..42fa72033e 100644
--- a/src/Microsoft.ML.Transforms/Text/WordEmbeddingsExtractor.cs
+++ b/src/Microsoft.ML.Transforms/Text/WordEmbeddingsExtractor.cs
@@ -284,7 +284,7 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
         private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Schema inputSchema)
             => Create(env, ctx).MakeRowMapper(inputSchema);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/Text/WordTokenizing.cs b/src/Microsoft.ML.Transforms/Text/WordTokenizing.cs
index 1d46ed420e..bf05a60e84 100644
--- a/src/Microsoft.ML.Transforms/Text/WordTokenizing.cs
+++ b/src/Microsoft.ML.Transforms/Text/WordTokenizing.cs
@@ -150,7 +150,7 @@ private WordTokenizingTransformer(IHost host, ModelLoadContext ctx) :
         private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx, IDataView input)
             => Create(env, ctx).MakeDataTransform(input);
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
diff --git a/src/Microsoft.ML.Transforms/UngroupTransform.cs b/src/Microsoft.ML.Transforms/UngroupTransform.cs
index 2dd0112dab..e0a594d8d6 100644
--- a/src/Microsoft.ML.Transforms/UngroupTransform.cs
+++ b/src/Microsoft.ML.Transforms/UngroupTransform.cs
@@ -138,7 +138,7 @@ private UngroupTransform(IHost host, ModelLoadContext ctx, IDataView input)
             _ungroupBinding = UngroupBinding.Create(ctx, host, input.Schema);
         }
 
-        public override void Save(ModelSaveContext ctx)
+        private protected override void SaveModel(ModelSaveContext ctx)
         {
             Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
@@ -365,7 +365,7 @@ public static UngroupBinding Create(ModelLoadContext ctx, IExceptionContext ectx
                 return new UngroupBinding(ectx, inputSchema, mode, pivotColumns);
             }
 
-            public void Save(ModelSaveContext ctx)
+            internal void Save(ModelSaveContext ctx)
             {
                 _ectx.AssertValue(ctx);
 
diff --git a/test/Microsoft.ML.CodeAnalyzer.Tests/Helpers/DiagnosticVerifier.cs b/test/Microsoft.ML.CodeAnalyzer.Tests/Helpers/DiagnosticVerifier.cs
index 7bf7e5693f..248a4645f5 100644
--- a/test/Microsoft.ML.CodeAnalyzer.Tests/Helpers/DiagnosticVerifier.cs
+++ b/test/Microsoft.ML.CodeAnalyzer.Tests/Helpers/DiagnosticVerifier.cs
@@ -15,6 +15,7 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.Transforms.Conversions;
 using Xunit;
 
 namespace Microsoft.ML.CodeAnalyzer.Tests.Helpers
@@ -267,7 +268,7 @@ private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diag
 
         private static readonly MetadataReference MSDataDataViewReference = RefFromType<IDataView>();
         private static readonly MetadataReference MLNetCoreReference = RefFromType<IHostEnvironment>();
-        private static readonly MetadataReference MLNetDataReference = RefFromType<Model.ModelLoadContext>();
+        private static readonly MetadataReference MLNetDataReference = RefFromType<MLContext>();
         private static readonly MetadataReference MLNetStaticPipeReference = RefFromType<CategoricalHashStaticExtensions.OneHotHashVectorOutputKind>();
 
         protected static MetadataReference RefFromType<TType>()

From 7dfadca77876d361cb3560e3fba61ae86204164c Mon Sep 17 00:00:00 2001
From: Rogan Carr <rogan.carr@hotmail.com>
Date: Fri, 8 Feb 2019 22:44:26 -0800
Subject: [PATCH 06/23] Add a project for functional tests without visibility
 into internals of ML.NET (#2470)

Adding a project for functional tests without internal access to the ML.NET Library.
---
 Microsoft.ML.sln                              | 15 ++++++
 build/Dependencies.props                      |  2 +
 .../SamplesDatasetUtils.cs                    |  7 ++-
 test/Microsoft.ML.Functional.Tests/Common.cs  | 24 +++++++++
 .../Microsoft.ML.Functional.Tests.csproj      | 52 ++++++++++++++++++
 .../Prediction.cs                             | 53 +++++++++++++++++++
 .../Validation.cs                             | 53 +++++++++++++++++++
 .../Microsoft.ML.OnnxTransformTest.csproj     |  2 +-
 test/Microsoft.ML.TestFramework/Datasets.cs   | 19 ++++++-
 .../Microsoft.ML.Tests.csproj                 |  4 +-
 .../Api/Estimators/CrossValidation.cs         | 36 -------------
 .../Estimators/ReconfigurablePrediction.cs    | 47 ----------------
 12 files changed, 226 insertions(+), 88 deletions(-)
 create mode 100644 test/Microsoft.ML.Functional.Tests/Common.cs
 create mode 100644 test/Microsoft.ML.Functional.Tests/Microsoft.ML.Functional.Tests.csproj
 create mode 100644 test/Microsoft.ML.Functional.Tests/Prediction.cs
 create mode 100644 test/Microsoft.ML.Functional.Tests/Validation.cs
 delete mode 100644 test/Microsoft.ML.Tests/Scenarios/Api/Estimators/CrossValidation.cs
 delete mode 100644 test/Microsoft.ML.Tests/Scenarios/Api/Estimators/ReconfigurablePrediction.cs

diff --git a/Microsoft.ML.sln b/Microsoft.ML.sln
index 9d969b6be2..2b9bcb6cf9 100644
--- a/Microsoft.ML.sln
+++ b/Microsoft.ML.sln
@@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.TestFramework"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Predictor.Tests", "test\Microsoft.ML.Predictor.Tests\Microsoft.ML.Predictor.Tests.csproj", "{6B047E09-39C9-4583-96F3-685D84CA4117}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Functional.Tests", "test\Microsoft.ML.Functional.Tests\Microsoft.ML.Functional.Tests.csproj", "{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.ResultProcessor", "src\Microsoft.ML.ResultProcessor\Microsoft.ML.ResultProcessor.csproj", "{3769FCC3-9AFF-4C37-97E9-6854324681DF}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.FastTree", "src\Microsoft.ML.FastTree\Microsoft.ML.FastTree.csproj", "{B7B593C5-FB8C-4ADA-A638-5B53B47D087E}"
@@ -928,6 +930,18 @@ Global
 		{5E920CAC-5A28-42FB-936E-49C472130953}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU
 		{5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU
 		{5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug-Intrinsics|Any CPU.ActiveCfg = Debug-Intrinsics|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug-Intrinsics|Any CPU.Build.0 = Debug-Intrinsics|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug-netfx|Any CPU.ActiveCfg = Debug-netfx|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Debug-netfx|Any CPU.Build.0 = Debug-netfx|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release-Intrinsics|Any CPU.ActiveCfg = Release-Intrinsics|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -1011,6 +1025,7 @@ Global
 		{85D0CAFD-2FE8-496A-88C7-585D35B94243} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
 		{31D38B21-102B-41C0-9E0A-2FE0BF68D123} = {D3D38B03-B557-484D-8348-8BADEE4DF592}
 		{5E920CAC-5A28-42FB-936E-49C472130953} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
+		{CFED9F0C-FF81-4C96-8D5E-0436264CA7B5} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {41165AF1-35BB-4832-A189-73060F82B01D}
diff --git a/build/Dependencies.props b/build/Dependencies.props
index 896ca68978..9d2174267b 100644
--- a/build/Dependencies.props
+++ b/build/Dependencies.props
@@ -43,6 +43,8 @@
   <PropertyGroup>
     <BenchmarkDotNetVersion>0.11.3</BenchmarkDotNetVersion>
     <MicrosoftMLTestModelsPackageVersion>0.0.3-test</MicrosoftMLTestModelsPackageVersion>
+    <MicrosoftMLTensorFlowTestModelsVersion>0.0.7-test</MicrosoftMLTensorFlowTestModelsVersion>
+    <MicrosoftMLOnnxTestModelsVersion>0.0.4-test</MicrosoftMLOnnxTestModelsVersion>
   </PropertyGroup>
 
 </Project>
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index 000bf48f95..17ce2e3ab7 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -17,7 +17,12 @@ public static class DatasetUtils
         /// Downloads the housing dataset from the ML.NET repo.
         /// </summary>
         public static string DownloadHousingRegressionDataset()
-        => Download("https://raw.githubusercontent.com/dotnet/machinelearning/024bd4452e1d3660214c757237a19d6123f951ca/test/data/housing.txt", "housing.txt");
+        {
+            var fileName = "housing.txt";
+            if (!File.Exists(fileName))
+                Download("https://raw.githubusercontent.com/dotnet/machinelearning/024bd4452e1d3660214c757237a19d6123f951ca/test/data/housing.txt", fileName);
+            return fileName;
+        }
 
         public static IDataView LoadHousingRegressionDataset(MLContext mlContext)
         {
diff --git a/test/Microsoft.ML.Functional.Tests/Common.cs b/test/Microsoft.ML.Functional.Tests/Common.cs
new file mode 100644
index 0000000000..29088298d3
--- /dev/null
+++ b/test/Microsoft.ML.Functional.Tests/Common.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Data.DataView;
+using Microsoft.ML.Data;
+using Microsoft.ML.SamplesUtils;
+using Microsoft.ML.Trainers.HalLearners;
+using Xunit;
+
+namespace Microsoft.ML.Functional.Tests
+{
+    internal static class Common
+    {
+        public static void CheckMetrics(RegressionMetrics metrics)
+        {
+            // Perform sanity checks on the metrics
+            Assert.True(metrics.Rms >= 0);
+            Assert.True(metrics.L1 >= 0);
+            Assert.True(metrics.L2 >= 0);
+            Assert.True(metrics.RSquared <= 1);
+        }
+    }
+}
diff --git a/test/Microsoft.ML.Functional.Tests/Microsoft.ML.Functional.Tests.csproj b/test/Microsoft.ML.Functional.Tests/Microsoft.ML.Functional.Tests.csproj
new file mode 100644
index 0000000000..106db8f36c
--- /dev/null
+++ b/test/Microsoft.ML.Functional.Tests/Microsoft.ML.Functional.Tests.csproj
@@ -0,0 +1,52 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <!-- We are turning off strong naming to ensure we never add `InternalsVisibleTo` for these tests -->
+    <SignAssembly>false</SignAssembly>
+    <PublicSign>false</PublicSign>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Microsoft.ML.Data\Microsoft.ML.Data.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.Ensemble\Microsoft.ML.Ensemble.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.EntryPoints\Microsoft.ML.EntryPoints.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.HalLearners\Microsoft.ML.HalLearners.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.ImageAnalytics\Microsoft.ML.ImageAnalytics.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.KMeansClustering\Microsoft.ML.KMeansClustering.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.SamplesUtils\Microsoft.ML.SamplesUtils.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.LightGBM\Microsoft.ML.LightGBM.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.Maml\Microsoft.ML.Maml.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.OnnxTransform\Microsoft.ML.OnnxTransform.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.PCA\Microsoft.ML.PCA.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.KMeansClustering\Microsoft.ML.KMeansClustering.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.Recommender\Microsoft.ML.Recommender.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.StandardLearners\Microsoft.ML.StandardLearners.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.Onnx\Microsoft.ML.Onnx.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.StaticPipe\Microsoft.ML.StaticPipe.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.TensorFlow.StaticPipe\Microsoft.ML.TensorFlow.StaticPipe.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.TensorFlow\Microsoft.ML.TensorFlow.csproj" />
+    <ProjectReference Include="..\..\src\Microsoft.ML.TimeSeries\Microsoft.ML.TimeSeries.csproj" />
+    <ProjectReference Include="..\Microsoft.ML.TestFramework\Microsoft.ML.TestFramework.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <NativeAssemblyReference Include="CpuMathNative" />
+    <NativeAssemblyReference Include="FastTreeNative" />
+    <NativeAssemblyReference Include="FactorizationMachineNative" />
+    <NativeAssemblyReference Include="MatrixFactorizationNative" />
+    <NativeAssemblyReference Include="LdaNative" />
+    <NativeAssemblyReference Include="SymSgdNative" />
+    <NativeAssemblyReference Include="MklProxyNative" />
+    <NativeAssemblyReference Include="MklImports" />
+  </ItemGroup>
+
+  <!-- TensorFlow is 64-bit only -->
+  <ItemGroup Condition="'$(NativeTargetArchitecture)' == 'x64'">
+    <NativeAssemblyReference Include="tensorflow" />
+    <NativeAssemblyReference Condition="'$(OS)' != 'Windows_NT'" Include="tensorflow_framework" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.ML.TensorFlow.TestModels" Version="$(MicrosoftMLTensorFlowTestModelsVersion)" />
+    <PackageReference Include="Microsoft.ML.Onnx.TestModels" Version="$(MicrosoftMLOnnxTestModelsVersion)" />
+  </ItemGroup>
+</Project>
diff --git a/test/Microsoft.ML.Functional.Tests/Prediction.cs b/test/Microsoft.ML.Functional.Tests/Prediction.cs
new file mode 100644
index 0000000000..7e0ff2eb44
--- /dev/null
+++ b/test/Microsoft.ML.Functional.Tests/Prediction.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework;
+using Xunit;
+
+namespace Microsoft.ML.Functional.Tests
+{
+    public class PredictionScenarios
+    {
+        /// <summary>
+        /// Reconfigurable predictions: The following should be possible: A user trains a binary classifier,
+        /// and through the test evaluator gets a PR curve, the based on the PR curve picks a new threshold
+        /// and configures the scorer (or more precisely instantiates a new scorer over the same model parameters)
+        /// with some threshold derived from that.
+        /// </summary>
+        [Fact]
+        public void ReconfigurablePrediction()
+        {
+            var mlContext = new MLContext(seed: 789);
+
+            // Get the dataset, create a train and test
+            var data = mlContext.Data.CreateTextLoader(TestDatasets.housing.GetLoaderColumns(), hasHeader: true)
+                .Read(BaseTestClass.GetDataPath(TestDatasets.housing.trainFilename));
+            (var train, var test) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.2);
+
+            // Create a pipeline to train on the housing data
+            var pipeline = mlContext.Transforms.Concatenate("Features", new string[] {
+                    "CrimesPerCapita", "PercentResidental", "PercentNonRetail", "CharlesRiver", "NitricOxides", "RoomsPerDwelling",
+                    "PercentPre40s", "EmploymentDistance", "HighwayDistance", "TaxRate", "TeacherRatio"})
+                .Append(mlContext.Transforms.CopyColumns("Label", "MedianHomeValue"))
+                .Append(mlContext.Regression.Trainers.OrdinaryLeastSquares());
+
+            var model = pipeline.Fit(train);
+
+            var scoredTest = model.Transform(test);
+            var metrics = mlContext.Regression.Evaluate(scoredTest);
+
+            Common.CheckMetrics(metrics);
+
+            // Todo #2465: Allow the setting of threshold and thresholdColumn for scoring.
+            // This is no longer possible in the API
+            //var newModel = new BinaryPredictionTransformer<IPredictorProducing<float>>(ml, model.Model, trainData.Schema, model.FeatureColumn, threshold: 0.01f, thresholdColumn: DefaultColumnNames.Probability);
+            //var newScoredTest = newModel.Transform(pipeline.Transform(testData));
+            //var newMetrics = mlContext.BinaryClassification.Evaluate(scoredTest);
+            // And the Threshold and ThresholdColumn properties are not settable.
+            //var predictor = model.LastTransformer;
+            //predictor.Threshold = 0.01; // Not possible
+        }
+    }
+}
diff --git a/test/Microsoft.ML.Functional.Tests/Validation.cs b/test/Microsoft.ML.Functional.Tests/Validation.cs
new file mode 100644
index 0000000000..b9bb617285
--- /dev/null
+++ b/test/Microsoft.ML.Functional.Tests/Validation.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Data.DataView;
+using Microsoft.ML.Data;
+using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework;
+using Microsoft.ML.Trainers.HalLearners;
+using Xunit;
+
+namespace Microsoft.ML.Functional.Tests
+{
+    public class ValidationScenarios
+    {
+        /// <summary>
+        /// Cross-validation: Have a mechanism to do cross validation, that is, you come up with
+        /// a data source (optionally with stratification column), come up with an instantiable transform
+        /// and trainer pipeline, and it will handle (1) splitting up the data, (2) training the separate
+        /// pipelines on in-fold data, (3) scoring on the out-fold data, (4) returning the set of
+        /// metrics, trained pipelines, and scored test data for each fold.
+        /// </summary>
+        [Fact]
+        void CrossValidation()
+        {
+            var mlContext = new MLContext(seed: 789);
+
+            // Get the dataset
+            var data = mlContext.Data.CreateTextLoader(TestDatasets.housing.GetLoaderColumns(), hasHeader: true)
+                .Read(BaseTestClass.GetDataPath(TestDatasets.housing.trainFilename));
+
+            // Create a pipeline to train on the sentiment data
+            var pipeline = mlContext.Transforms.Concatenate("Features", new string[] {
+                    "CrimesPerCapita", "PercentResidental", "PercentNonRetail", "CharlesRiver", "NitricOxides", "RoomsPerDwelling",
+                    "PercentPre40s", "EmploymentDistance", "HighwayDistance", "TaxRate", "TeacherRatio"})
+                .Append(mlContext.Transforms.CopyColumns("Label", "MedianHomeValue"))
+                .Append(mlContext.Regression.Trainers.OrdinaryLeastSquares());
+
+            // Compute the CV result
+            var cvResult = mlContext.Regression.CrossValidate(data, pipeline, numFolds: 5);
+
+            // Check that the results are valid
+            Assert.IsType<RegressionMetrics>(cvResult[0].metrics);
+            Assert.IsType<TransformerChain<RegressionPredictionTransformer<OlsLinearRegressionModelParameters>>>(cvResult[0].model);
+            Assert.True(cvResult[0].scoredTestData is IDataView);
+            Assert.Equal(5, cvResult.Length);
+
+            // And validate the metrics
+            foreach (var result in cvResult)
+                Common.CheckMetrics(result.metrics);
+        }
+    }
+}
diff --git a/test/Microsoft.ML.OnnxTransformTest/Microsoft.ML.OnnxTransformTest.csproj b/test/Microsoft.ML.OnnxTransformTest/Microsoft.ML.OnnxTransformTest.csproj
index a153655b27..a5dbaca9f8 100644
--- a/test/Microsoft.ML.OnnxTransformTest/Microsoft.ML.OnnxTransformTest.csproj
+++ b/test/Microsoft.ML.OnnxTransformTest/Microsoft.ML.OnnxTransformTest.csproj
@@ -11,7 +11,7 @@
     <ProjectReference Include="..\Microsoft.ML.TestFramework\Microsoft.ML.TestFramework.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.ML.Onnx.TestModels" Version="0.0.4-test" />
+    <PackageReference Include="Microsoft.ML.Onnx.TestModels" Version="$(MicrosoftMLOnnxTestModelsVersion)" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/test/Microsoft.ML.TestFramework/Datasets.cs b/test/Microsoft.ML.TestFramework/Datasets.cs
index 6d6ba61191..1bdfa5048b 100644
--- a/test/Microsoft.ML.TestFramework/Datasets.cs
+++ b/test/Microsoft.ML.TestFramework/Datasets.cs
@@ -158,7 +158,24 @@ public static class TestDatasets
             name = "housing",
             trainFilename = "housing.txt",
             testFilename = "housing.txt",
-            loaderSettings = "loader=Text{col=Label:0 col=Features:~ header=+}"
+            loaderSettings = "loader=Text{col=Label:0 col=Features:~ header=+}",
+            GetLoaderColumns = () =>
+            {
+                return new[] {
+                    new TextLoader.Column("MedianHomeValue", DataKind.R4, 0),
+                    new TextLoader.Column("CrimesPerCapita", DataKind.R4, 1),
+                    new TextLoader.Column("PercentResidental", DataKind.R4, 2),
+                    new TextLoader.Column("PercentNonRetail", DataKind.R4, 3),
+                    new TextLoader.Column("CharlesRiver", DataKind.R4, 4),
+                    new TextLoader.Column("NitricOxides", DataKind.R4, 5),
+                    new TextLoader.Column("RoomsPerDwelling", DataKind.R4, 6),
+                    new TextLoader.Column("PercentPre40s", DataKind.R4, 7),
+                    new TextLoader.Column("EmploymentDistance", DataKind.R4, 8),
+                    new TextLoader.Column("HighwayDistance", DataKind.R4, 9),
+                    new TextLoader.Column("TaxRate", DataKind.R4, 10),
+                    new TextLoader.Column("TeacherRatio", DataKind.R4, 11),
+                };
+            }
         };
 
         public static TestDataset generatedRegressionDatasetmacro = new TestDataset
diff --git a/test/Microsoft.ML.Tests/Microsoft.ML.Tests.csproj b/test/Microsoft.ML.Tests/Microsoft.ML.Tests.csproj
index 2d56666b7f..37f4b25c1e 100644
--- a/test/Microsoft.ML.Tests/Microsoft.ML.Tests.csproj
+++ b/test/Microsoft.ML.Tests/Microsoft.ML.Tests.csproj
@@ -46,7 +46,7 @@
     <NativeAssemblyReference Condition="'$(OS)' != 'Windows_NT'" Include="tensorflow_framework" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.ML.TensorFlow.TestModels" Version="0.0.7-test" />
-    <PackageReference Include="Microsoft.ML.Onnx.TestModels" Version="0.0.2-test" />
+    <PackageReference Include="Microsoft.ML.TensorFlow.TestModels" Version="$(MicrosoftMLTensorFlowTestModelsVersion)" />
+    <PackageReference Include="Microsoft.ML.Onnx.TestModels" Version="$(MicrosoftMLOnnxTestModelsVersion)" />
   </ItemGroup>
 </Project>
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/CrossValidation.cs b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/CrossValidation.cs
deleted file mode 100644
index a4e3afc2cc..0000000000
--- a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/CrossValidation.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.ML.RunTests;
-using Microsoft.ML.Trainers;
-using Xunit;
-
-namespace Microsoft.ML.Tests.Scenarios.Api
-{
-    public partial class ApiScenariosTests
-    {
-        /// <summary>
-        /// Cross-validation: Have a mechanism to do cross validation, that is, you come up with
-        /// a data source (optionally with stratification column), come up with an instantiable transform
-        /// and trainer pipeline, and it will handle (1) splitting up the data, (2) training the separate
-        /// pipelines on in-fold data, (3) scoring on the out-fold data, (4) returning the set of
-        /// evaluations and optionally trained pipes. (People always want metrics out of xfold,
-        /// they sometimes want the actual models too.)
-        /// </summary>
-        [Fact]
-        void CrossValidation()
-        {
-            var ml = new MLContext(seed: 1, conc: 1);
-
-            var data = ml.Data.ReadFromTextFile<SentimentData>(GetDataPath(TestDatasets.Sentiment.trainFilename), hasHeader: true);
-
-            // Pipeline.
-            var pipeline = ml.Transforms.Text.FeaturizeText("Features", "SentimentText")
-                    .Append(ml.BinaryClassification.Trainers.StochasticDualCoordinateAscent(
-                        new SdcaBinaryTrainer.Options { ConvergenceTolerance = 1f, NumThreads = 1, }));
-
-            var cvResult = ml.BinaryClassification.CrossValidate(data, pipeline);
-        }
-    }
-}
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/ReconfigurablePrediction.cs b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/ReconfigurablePrediction.cs
deleted file mode 100644
index 254dd73e45..0000000000
--- a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/ReconfigurablePrediction.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.ML.Data;
-using Microsoft.ML.RunTests;
-using Microsoft.ML.Trainers;
-using Xunit;
-
-namespace Microsoft.ML.Tests.Scenarios.Api
-{
-    public partial class ApiScenariosTests
-    {
-        /// <summary>
-        /// Reconfigurable predictions: The following should be possible: A user trains a binary classifier,
-        /// and through the test evaluator gets a PR curve, the based on the PR curve picks a new threshold
-        /// and configures the scorer (or more precisely instantiates a new scorer over the same predictor)
-        /// with some threshold derived from that.
-        /// </summary>
-        [Fact]
-        public void ReconfigurablePrediction()
-        {
-            var ml = new MLContext(seed: 1, conc: 1);
-            var dataReader = ml.Data.ReadFromTextFile<SentimentData>(GetDataPath(TestDatasets.Sentiment.trainFilename), hasHeader: true);
-
-            var data = ml.Data.ReadFromTextFile<SentimentData>(GetDataPath(TestDatasets.Sentiment.trainFilename), hasHeader: true);
-            var testData = ml.Data.ReadFromTextFile<SentimentData>(GetDataPath(TestDatasets.Sentiment.testFilename), hasHeader: true);
-
-            // Pipeline.
-            var pipeline = ml.Transforms.Text.FeaturizeText("Features", "SentimentText")
-                .Fit(data);
-
-            var trainer = ml.BinaryClassification.Trainers.StochasticDualCoordinateAscent(
-                new SdcaBinaryTrainer.Options { NumThreads = 1 });
-
-            var trainData = ml.Data.Cache(pipeline.Transform(data)); // Cache the data right before the trainer to boost the training speed.
-            var model = trainer.Fit(trainData);
-
-            var scoredTest = model.Transform(pipeline.Transform(testData));
-            var metrics = ml.BinaryClassification.Evaluate(scoredTest);
-
-            var newModel = new BinaryPredictionTransformer<IPredictorProducing<float>>(ml, model.Model, trainData.Schema, model.FeatureColumn, threshold: 0.01f, thresholdColumn: DefaultColumnNames.Probability);
-            var newScoredTest = newModel.Transform(pipeline.Transform(testData));
-            var newMetrics = ml.BinaryClassification.Evaluate(scoredTest);
-        }
-    }
-}

From 07580a8b7b9781b15e6009caac502c0ebb637705 Mon Sep 17 00:00:00 2001
From: Marek Linka <mareklinka@users.noreply.github.com>
Date: Sat, 9 Feb 2019 17:29:30 +0100
Subject: [PATCH 07/23] Add analyzer for detecting BestFriend usages on public
 declarations (#2434)

* Add analyzer for detecting BestFriend usages on public declaration

* Rewrite analyzer as Symbol analyzer

* Add test to prove IntClass.PubMember reports a warning
---
 .../Code/BestFriendOnPublicDeclarationTest.cs |  63 +++++++++++
 .../BestFriendOnPublicDeclaration.cs          | 107 ++++++++++++++++++
 .../BestFriendOnPublicDeclarationsAnalyzer.cs |  70 ++++++++++++
 3 files changed, 240 insertions(+)
 create mode 100644 test/Microsoft.ML.CodeAnalyzer.Tests/Code/BestFriendOnPublicDeclarationTest.cs
 create mode 100644 test/Microsoft.ML.CodeAnalyzer.Tests/Resources/BestFriendOnPublicDeclaration.cs
 create mode 100644 tools-local/Microsoft.ML.InternalCodeAnalyzer/BestFriendOnPublicDeclarationsAnalyzer.cs

diff --git a/test/Microsoft.ML.CodeAnalyzer.Tests/Code/BestFriendOnPublicDeclarationTest.cs b/test/Microsoft.ML.CodeAnalyzer.Tests/Code/BestFriendOnPublicDeclarationTest.cs
new file mode 100644
index 0000000000..ddd8fb5d72
--- /dev/null
+++ b/test/Microsoft.ML.CodeAnalyzer.Tests/Code/BestFriendOnPublicDeclarationTest.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Reflection;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.ML.CodeAnalyzer.Tests.Helpers;
+using Xunit;
+
+namespace Microsoft.ML.InternalCodeAnalyzer.Tests
+{
+    public sealed class BestFriendOnPublicDeclarationTest : DiagnosticVerifier<BestFriendOnPublicDeclarationsAnalyzer>
+    {
+        private readonly Lazy<string> SourceAttribute = TestUtils.LazySource("BestFriendAttribute.cs");
+        private readonly Lazy<string> SourceDeclaration = TestUtils.LazySource("BestFriendOnPublicDeclaration.cs");
+
+        [Fact]
+        public void BestFriendOnPublicDeclaration()
+        {
+            Solution solution = null;
+            var projA = CreateProject("ProjectA", ref solution, SourceDeclaration.Value, SourceAttribute.Value);
+
+            var analyzer = new BestFriendOnPublicDeclarationsAnalyzer();
+
+            var refs = new List<MetadataReference> {
+                RefFromType<object>(), RefFromType<Attribute>(),
+                MetadataReference.CreateFromFile(Assembly.Load("netstandard, Version=2.0.0.0").Location),
+                MetadataReference.CreateFromFile(Assembly.Load("System.Runtime, Version=0.0.0.0").Location)
+            };
+
+            var comp = projA.GetCompilationAsync().Result.WithReferences(refs.ToArray());
+            var compilationWithAnalyzers = comp.WithAnalyzers(ImmutableArray.Create((DiagnosticAnalyzer)analyzer));
+            var allDiags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
+
+            var projectTrees = new HashSet<SyntaxTree>(projA.Documents.Select(r => r.GetSyntaxTreeAsync().Result));
+            var diags = allDiags
+                .Where(d => d.Location == Location.None || d.Location.IsInMetadata || projectTrees.Contains(d.Location.SourceTree))
+                .OrderBy(d => d.Location.SourceSpan.Start).ToArray();
+
+            var diag = analyzer.SupportedDiagnostics[0];
+            var expected = new DiagnosticResult[] {
+                diag.CreateDiagnosticResult(8, 6, "PublicClass"),
+                diag.CreateDiagnosticResult(11, 10, "PublicField"),
+                diag.CreateDiagnosticResult(14, 10, "PublicProperty"),
+                diag.CreateDiagnosticResult(20, 10, "PublicMethod"),
+                diag.CreateDiagnosticResult(26, 10, "PublicDelegate"),
+                diag.CreateDiagnosticResult(29, 10, "PublicClass"),
+                diag.CreateDiagnosticResult(35, 6, "PublicStruct"),
+                diag.CreateDiagnosticResult(40, 6, "PublicEnum"),
+                diag.CreateDiagnosticResult(47, 6, "PublicInterface"),
+                diag.CreateDiagnosticResult(102, 10, "PublicMethod")
+            };
+
+            VerifyDiagnosticResults(diags, analyzer, expected);
+        }
+    }
+}
+
diff --git a/test/Microsoft.ML.CodeAnalyzer.Tests/Resources/BestFriendOnPublicDeclaration.cs b/test/Microsoft.ML.CodeAnalyzer.Tests/Resources/BestFriendOnPublicDeclaration.cs
new file mode 100644
index 0000000000..1515f21e1f
--- /dev/null
+++ b/test/Microsoft.ML.CodeAnalyzer.Tests/Resources/BestFriendOnPublicDeclaration.cs
@@ -0,0 +1,107 @@
+using System;
+using Microsoft.ML;
+
+namespace TestNamespace
+{
+    // all of the best friend declaration should fail the diagnostic
+
+    [BestFriend]
+    public class PublicClass
+    {
+        [BestFriend]
+        public int PublicField;
+
+        [BestFriend]
+        public string PublicProperty
+        {
+            get { return string.Empty; }
+        }
+
+        [BestFriend]
+        public bool PublicMethod()
+        {
+            return true;
+        }
+
+        [BestFriend]
+        public delegate string PublicDelegate();
+
+        [BestFriend]
+        public PublicClass()
+        {
+        }
+    }
+
+    [BestFriend]
+    public struct PublicStruct
+    {
+    }
+
+    [BestFriend]
+    public enum PublicEnum
+    {
+        EnumValue1,
+        EnumValue2
+    }
+
+    [BestFriend]
+    public interface PublicInterface
+    {
+    }
+
+    // these should work
+
+    [BestFriend]
+    internal class InternalClass
+    {
+        [BestFriend]
+        internal int InternalField;
+
+        [BestFriend]
+        internal string InternalProperty
+        {
+            get { return string.Empty; }
+        }
+
+        [BestFriend]
+        internal bool InternalMethod()
+        {
+            return true;
+        }
+
+        [BestFriend]
+        internal delegate string InternalDelegate();
+
+        [BestFriend]
+        internal InternalClass()
+        {
+        }
+    }
+
+    [BestFriend]
+    internal struct InternalStruct
+    {
+    }
+
+    [BestFriend]
+    internal enum InternalEnum
+    {
+        EnumValue1,
+        EnumValue2
+    }
+
+    [BestFriend]
+    internal interface InternalInterface
+    {
+    }
+
+    // this should fail the diagnostic
+    // a repro for https://github.com/dotnet/machinelearning/pull/2434#discussion_r254770946
+    internal class InternalClassWithPublicMember
+    {
+        [BestFriend]
+        public void PublicMethod()
+        {
+        }
+    }
+}
diff --git a/tools-local/Microsoft.ML.InternalCodeAnalyzer/BestFriendOnPublicDeclarationsAnalyzer.cs b/tools-local/Microsoft.ML.InternalCodeAnalyzer/BestFriendOnPublicDeclarationsAnalyzer.cs
new file mode 100644
index 0000000000..658e957c16
--- /dev/null
+++ b/tools-local/Microsoft.ML.InternalCodeAnalyzer/BestFriendOnPublicDeclarationsAnalyzer.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Microsoft.ML.InternalCodeAnalyzer
+{
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class BestFriendOnPublicDeclarationsAnalyzer : DiagnosticAnalyzer
+    {
+        private const string Category = "Access";
+        internal const string DiagnosticId = "MSML_BestFriendOnPublicDeclaration";
+
+        private const string Title = "Public declarations should not have " + AttributeName + " attribute.";
+        private const string Format = "The " + AttributeName + " should not be applied to publicly visible members.";
+
+        private const string Description =
+            "The " + AttributeName + " attribute is not valid on public identifiers.";
+
+        private static DiagnosticDescriptor Rule =
+            new DiagnosticDescriptor(DiagnosticId, Title, Format, Category,
+                DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
+
+        private const string AttributeName = "Microsoft.ML.BestFriendAttribute";
+
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Rule);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.EnableConcurrentExecution();
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+
+            context.RegisterCompilationStartAction(CompilationStart);
+        }
+
+        private void CompilationStart(CompilationStartAnalysisContext context)
+        {
+            var list = new List<string> { AttributeName, "Microsoft.ML.Internal.CpuMath.Core.BestFriendAttribute" };
+
+            foreach (var attributeName in list)
+            {
+                var attribute = context.Compilation.GetTypeByMetadataName(attributeName);
+
+                if (attribute == null)
+                    continue;
+                
+                context.RegisterSymbolAction(c => AnalyzeCore(c, attribute), SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Field, SymbolKind.Property);
+            }
+        }
+
+        private void AnalyzeCore(SymbolAnalysisContext context, INamedTypeSymbol attributeType)
+        {
+            if (context.Symbol.DeclaredAccessibility != Accessibility.Public)
+                return;
+
+            var attribute = context.Symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass == attributeType);
+            if (attribute == null)
+                return;
+
+            var diagnostic = Diagnostic.Create(Rule, attribute.ApplicationSyntaxReference.GetSyntax().GetLocation(), context.Symbol.Name);
+            context.ReportDiagnostic(diagnostic);
+        }
+    }
+}
\ No newline at end of file

From 7d7ebb6e414bb6c7740732e351d8154d248d2ee0 Mon Sep 17 00:00:00 2001
From: Zeeshan Ahmed <38438266+zeahmed@users.noreply.github.com>
Date: Sat, 9 Feb 2019 19:57:05 -0800
Subject: [PATCH 08/23] TensorFlow: Fixed shape issue where unknown shape will
 be induced from data. (#2475)

---
 build/Dependencies.props                      |   2 +-
 .../TensorflowTransform.cs                    |  45 +++----
 .../TensorflowTests.cs                        | 117 +++++++++++++++++-
 3 files changed, 135 insertions(+), 29 deletions(-)

diff --git a/build/Dependencies.props b/build/Dependencies.props
index 9d2174267b..221d1e6552 100644
--- a/build/Dependencies.props
+++ b/build/Dependencies.props
@@ -43,7 +43,7 @@
   <PropertyGroup>
     <BenchmarkDotNetVersion>0.11.3</BenchmarkDotNetVersion>
     <MicrosoftMLTestModelsPackageVersion>0.0.3-test</MicrosoftMLTestModelsPackageVersion>
-    <MicrosoftMLTensorFlowTestModelsVersion>0.0.7-test</MicrosoftMLTensorFlowTestModelsVersion>
+    <MicrosoftMLTensorFlowTestModelsVersion>0.0.10-test</MicrosoftMLTensorFlowTestModelsVersion>
     <MicrosoftMLOnnxTestModelsVersion>0.0.4-test</MicrosoftMLOnnxTestModelsVersion>
   </PropertyGroup>
 
diff --git a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
index f87c2da37c..cfe041445e 100644
--- a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
+++ b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
@@ -667,15 +667,6 @@ internal static (TFDataType[] tfInputTypes, TFShape[] tfInputShapes) GetInputInf
                 var tfInput = new TFOutput(session.Graph[inputs[i]]);
                 tfInputTypes[i] = tfInput.OutputType;
                 tfInputShapes[i] = session.Graph.GetTensorShape(tfInput);
-                if (tfInputShapes[i].NumDimensions != -1)
-                {
-                    var newShape = new long[tfInputShapes[i].NumDimensions];
-                    newShape[0] = tfInputShapes[i][0] == -1 ? BatchSize : tfInputShapes[i][0];
-
-                    for (int j = 1; j < tfInputShapes[i].NumDimensions; j++)
-                        newShape[j] = tfInputShapes[i][j];
-                    tfInputShapes[i] = new TFShape(newShape);
-                }
             }
             return (tfInputTypes, tfInputShapes);
         }
@@ -698,7 +689,14 @@ internal static (TFDataType[] tfOutputTypes, ColumnType[] outputTypes) GetOutput
             {
                 var tfOutput = new TFOutput(session.Graph[outputs[i]]);
                 var shape = session.Graph.GetTensorShape(tfOutput);
+
+                // The transformer can only retreive the output as fixed length vector with shape of kind [-1, d1, d2, d3, ...]
+                // i.e. the first dimension (if unknown) is assumed to be batch dimension.
+                // If there are other dimension that are unknown the transformer will return a variable length vector.
+                // This is the work around in absence of reshape transformer.
                 int[] dims = shape.NumDimensions > 0 ? shape.ToIntArray().Skip(shape[0] == -1 ? 1 : 0).ToArray() : new[] { 0 };
+                for (int j = 0; j < dims.Length; j++)
+                    dims[j] = dims[j] == -1 ? 0 : dims[j];
                 var type = TensorFlowUtils.Tf2MlNetType(tfOutput.OutputType);
                 outputTypes[i] = new VectorType(type, dims);
                 tfOutputTypes[i] = tfOutput.OutputType;
@@ -837,14 +835,22 @@ public Mapper(TensorFlowTransformer parent, Schema inputSchema) :
                     var originalShape = _parent.TFInputShapes[i];
                     var shape = originalShape.ToIntArray();
 
-                    var colTypeDims = vecType.Dimensions.Prepend(1).Select(dim => (long)dim).ToArray();
+                    var colTypeDims = vecType.Dimensions.Select(dim => (long)dim).ToArray();
                     if (shape == null)
                         _fullySpecifiedShapes[i] = new TFShape(colTypeDims);
-                    else if (vecType.Dimensions.Length == 1)
+                    else
                     {
                         // If the column is one dimension we make sure that the total size of the TF shape matches.
                         // Compute the total size of the known dimensions of the shape.
-                        int valCount = shape.Where(x => x > 0).Aggregate((x, y) => x * y);
+                        int valCount = 1;
+                        int numOfUnkDim = 0;
+                        foreach (var s in shape)
+                        {
+                            if (s > 0)
+                                valCount *= s;
+                            else
+                                numOfUnkDim++;
+                        }
                         // The column length should be divisible by this, so that the other dimensions can be integral.
                         int typeValueCount = type.GetValueCount();
                         if (typeValueCount % valCount != 0)
@@ -853,8 +859,8 @@ public Mapper(TensorFlowTransformer parent, Schema inputSchema) :
                         // If the shape is multi-dimensional, we should be able to create the length of the vector by plugging
                         // in a single value for the unknown shapes. For example, if the shape is [?,?,3], then there should exist a value
                         // d such that d*d*3 is equal to the length of the input column.
-                        var d = originalShape.NumDimensions > 2 ? Math.Pow(typeValueCount / valCount, 1.0 / (originalShape.NumDimensions - 2)) : 1;
-                        if (originalShape.NumDimensions > 2 && d - (int)d != 0)
+                        var d = numOfUnkDim > 0 ? Math.Pow(typeValueCount / valCount, 1.0 / numOfUnkDim) : 0;
+                        if (d - (int)d != 0)
                             throw Contracts.Except($"Input shape mismatch: Input '{_parent.Inputs[i]}' has shape {originalShape.ToString()}, but input data is of length {typeValueCount}.");
 
                         // Fill in the unknown dimensions.
@@ -863,17 +869,6 @@ public Mapper(TensorFlowTransformer parent, Schema inputSchema) :
                             l[ishape] = originalShape[ishape] == -1 ? (int)d : originalShape[ishape];
                         _fullySpecifiedShapes[i] = new TFShape(l);
                     }
-                    else
-                    {
-                        if (shape.Select((dim, j) => dim != -1 && dim != colTypeDims[j]).Any(b => b))
-                            throw Contracts.Except($"Input shape mismatch: Input '{_parent.Inputs[i]}' has shape {originalShape.ToString()}, but input data is {vecType.ToString()}.");
-
-                        // Fill in the unknown dimensions.
-                        var l = new long[originalShape.NumDimensions];
-                        for (int ishape = 0; ishape < originalShape.NumDimensions; ishape++)
-                            l[ishape] = originalShape[ishape] == -1 ? colTypeDims[ishape] : originalShape[ishape];
-                        _fullySpecifiedShapes[i] = new TFShape(l);
-                    }
                 }
             }
 
diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
index fd8d3183bb..398d426684 100644
--- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
+++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
@@ -74,6 +74,117 @@ public void TensorFlowTransformMatrixMultiplicationTest()
             }
         }
 
+        private class ShapeData
+        {
+            // Data will be passed as 1-D vector.
+            // Intended data shape [5], model shape [None]
+            [VectorType(5)]
+            public float[] OneDim;
+
+            // Data will be passed as flat vector.
+            // Intended data shape [2,2], model shape [2, None]
+            [VectorType(4)]
+            public float[] TwoDim;
+
+            // Data will be passed as 3-D vector.
+            // Intended data shape [1, 2, 2], model shape [1, None, 2]
+            [VectorType(1, 2, 2)]
+            public float[] ThreeDim;
+
+            // Data will be passed as flat vector.
+            // Intended data shape [1, 2, 2, 3], model shape [1, None, None, 3]
+            [VectorType(12)]
+            public float[] FourDim;
+
+            // Data will be passed as 4-D vector.
+            // Intended data shape [2, 2, 2, 2], model shape [2, 2, 2, 2]
+            [VectorType(2, 2, 2, 2)]
+            public float[] FourDimKnown;
+        }
+
+        private List<ShapeData> GetShapeData()
+        {
+            return new List<ShapeData>(new ShapeData[] {
+                        new ShapeData() {   OneDim = new[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f },
+                                            TwoDim = new[] { 1.0f, 2.0f, 3.0f, 4.0f },
+                                            ThreeDim = new[] { 11.0f, 12.0f, 13.0f, 14.0f },
+                                            FourDim = new[]{ 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f,
+                                                             27.0f, 28.0f, 29.0f, 30.0f, 31.0f, 32.0f },
+                                            FourDimKnown = new[]{ 41.0f , 42.0f, 43.0f, 44.0f, 45.0f, 46.0f, 47.0f, 48.0f,
+                                                                  49.0f , 50.0f, 51.0f, 52.0f, 53.0f, 54.0f, 55.0f, 56.0f}
+                                        },
+                        new ShapeData() {   OneDim = new[] { 100.1f, 100.2f, 100.3f, 100.4f, 100.5f },
+                                            TwoDim = new[] { 101.0f, 102.0f, 103.0f, 104.0f },
+                                            ThreeDim = new[] { 111.0f, 112.0f, 113.0f, 114.0f },
+                                            FourDim = new[]{ 121.0f, 122.0f, 123.0f, 124.0f, 125.0f, 126.0f,
+                                                             127.0f, 128.0f, 129.0f, 130.0f, 131.0f, 132.0f},
+                                            FourDimKnown = new[]{ 141.0f , 142.0f, 143.0f, 144.0f, 145.0f, 146.0f, 147.0f, 148.0f,
+                                                                  149.0f , 150.0f, 151.0f, 152.0f, 153.0f, 154.0f, 155.0f, 156.0f }
+                                        }
+                });
+        }
+
+        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        public void TensorFlowTransformInputShapeTest()
+        {
+            var modelLocation = "model_shape_test";
+            var mlContext = new MLContext(seed: 1, conc: 1);
+            var data = GetShapeData();
+            // Pipeline
+            var loader = mlContext.Data.ReadFromEnumerable(data);
+            var inputs = new string[] { "OneDim", "TwoDim", "ThreeDim", "FourDim", "FourDimKnown" };
+            var outputs = new string[] { "o_OneDim", "o_TwoDim", "o_ThreeDim", "o_FourDim", "o_FourDimKnown" };
+
+            var trans = mlContext.Transforms.ScoreTensorFlowModel(modelLocation, outputs, inputs).Fit(loader).Transform(loader);
+
+            using (var cursor = trans.GetRowCursorForAllColumns())
+            {
+                int outColIndex = 5;
+                var oneDimgetter = cursor.GetGetter<VBuffer<float>>(outColIndex);
+                var twoDimgetter = cursor.GetGetter<VBuffer<float>>(outColIndex + 1);
+                var threeDimgetter = cursor.GetGetter<VBuffer<float>>(outColIndex + 2);
+                var fourDimgetter = cursor.GetGetter<VBuffer<float>>(outColIndex + 3);
+                var fourDimKnowngetter = cursor.GetGetter<VBuffer<float>>(outColIndex + 4);
+
+                VBuffer<float> oneDim = default;
+                VBuffer<float> twoDim = default;
+                VBuffer<float> threeDim = default;
+                VBuffer<float> fourDim = default;
+                VBuffer<float> fourDimKnown = default;
+                foreach (var sample in data)
+                {
+                    Assert.True(cursor.MoveNext());
+
+                    oneDimgetter(ref oneDim);
+                    twoDimgetter(ref twoDim);
+                    threeDimgetter(ref threeDim);
+                    fourDimgetter(ref fourDim);
+                    fourDimKnowngetter(ref fourDimKnown);
+
+                    var oneDimValues = oneDim.GetValues();
+                    Assert.Equal(sample.OneDim.Length, oneDimValues.Length);
+                    Assert.True(oneDimValues.SequenceEqual(sample.OneDim));
+
+                    var twoDimValues = twoDim.GetValues();
+                    Assert.Equal(sample.TwoDim.Length, twoDimValues.Length);
+                    Assert.True(twoDimValues.SequenceEqual(sample.TwoDim));
+
+                    var threeDimValues = threeDim.GetValues();
+                    Assert.Equal(sample.ThreeDim.Length, threeDimValues.Length);
+                    Assert.True(threeDimValues.SequenceEqual(sample.ThreeDim));
+
+                    var fourDimValues = fourDim.GetValues();
+                    Assert.Equal(sample.FourDim.Length, fourDimValues.Length);
+                    Assert.True(fourDimValues.SequenceEqual(sample.FourDim));
+
+                    var fourDimKnownValues = fourDimKnown.GetValues();
+                    Assert.Equal(sample.FourDimKnown.Length, fourDimKnownValues.Length);
+                    Assert.True(fourDimKnownValues.SequenceEqual(sample.FourDimKnown));
+                }
+                Assert.False(cursor.MoveNext());
+            }
+        }
+
         private class TypesData
         {
             [VectorType(2)]
@@ -142,7 +253,7 @@ public void TensorFlowTransformInputOutputTypesTest()
 
             var loader = mlContext.Data.ReadFromEnumerable(data);
 
-            var inputs = new string[]{"f64", "f32", "i64", "i32", "i16", "i8", "u64", "u32", "u16", "u8","b"};
+            var inputs = new string[] { "f64", "f32", "i64", "i32", "i16", "i8", "u64", "u32", "u16", "u8", "b" };
             var outputs = new string[] { "o_f64", "o_f32", "o_i64", "o_i32", "o_i16", "o_i8", "o_u64", "o_u32", "o_u16", "o_u8", "o_b" };
             var trans = mlContext.Transforms.ScoreTensorFlowModel(model_location, outputs, inputs).Fit(loader).Transform(loader); ;
 
@@ -160,7 +271,7 @@ public void TensorFlowTransformInputOutputTypesTest()
                 var u8getter = cursor.GetGetter<VBuffer<byte>>(20);
                 var boolgetter = cursor.GetGetter<VBuffer<bool>>(21);
 
-               
+
                 VBuffer<double> f64 = default;
                 VBuffer<float> f32 = default;
                 VBuffer<long> i64 = default;
@@ -449,7 +560,7 @@ public void TensorFlowTransformMNISTLRTrainingTest()
                         ReTrain = true
                     }))
                     .Append(mlContext.Transforms.Concatenate("Features", "Prediction"))
-                    .Append(mlContext.Transforms.Conversion.MapValueToKey("KeyLabel","Label", maxNumKeys: 10))
+                    .Append(mlContext.Transforms.Conversion.MapValueToKey("KeyLabel", "Label", maxNumKeys: 10))
                     .Append(mlContext.MulticlassClassification.Trainers.LightGbm("KeyLabel", "Features"));
 
                 var trainedModel = pipe.Fit(trainData);

From a3b6522d6c82e61fe98e4e41fd661e936149ca55 Mon Sep 17 00:00:00 2001
From: Senja Filipi <sfilipi@users.noreply.github.com>
Date: Sun, 10 Feb 2019 00:11:33 -0800
Subject: [PATCH 09/23] Towards #2326 - removing some namespaces (#2442)

* removing the Microsoft.ML.Learners and the Microsoft.ML.Trainers.SymSGD namespaces.

* removing the Microsoft.ML.Internal.Internallearn.ResultProcessor and the Microsoft.ML.Trainers.FastTree.Internal namespaces.

* regenerating the catalog
---
 .../PermutationFeatureImportance/PFIHelper.cs |  3 ++-
 .../PfiBinaryClassificationExample.cs         |  2 +-
 .../Static/SDCARegression.cs                  |  2 +-
 .../EntryPoints/CreateEnsemble.cs             |  3 +--
 .../EntryPoints/DiversityMeasure.cs           |  4 +--
 .../EntryPoints/Ensemble.cs                   |  4 +--
 .../EntryPoints/FeatureSelector.cs            |  4 +--
 .../EntryPoints/OutputCombiner.cs             |  4 +--
 .../EntryPoints/PipelineEnsemble.cs           |  4 +--
 .../EntryPoints/SubModelSelector.cs           |  4 +--
 .../BestDiverseSelectorBinary.cs              |  3 ---
 .../BestDiverseSelectorMultiClass.cs          |  3 ---
 .../BestDiverseSelectorRegression.cs          |  3 ---
 .../SubsetSelector/BaseSubsetSelector.cs      |  1 -
 .../Trainer/Binary/EnsembleTrainer.cs         |  3 +--
 .../MulticlassDataPartitionEnsembleTrainer.cs |  3 +--
 .../Regression/RegressionEnsembleTrainer.cs   |  4 +--
 .../BinFile/BinFinder.cs                      |  2 +-
 .../BinFile/IniFileParserInterface.cs         |  2 +-
 src/Microsoft.ML.FastTree/BoostingFastTree.cs |  1 -
 src/Microsoft.ML.FastTree/Dataset/Dataset.cs  |  2 +-
 .../Dataset/DatasetUtils.cs                   |  2 +-
 .../Dataset/DenseIntArray.cs                  |  2 +-
 src/Microsoft.ML.FastTree/Dataset/Feature.cs  |  2 +-
 .../Dataset/FeatureFlock.cs                   |  2 +-
 .../Dataset/FeatureHistogram.cs               |  2 +-
 .../Dataset/FileObjectStore.cs                |  2 +-
 src/Microsoft.ML.FastTree/Dataset/IntArray.cs |  2 +-
 .../Dataset/NHotFeatureFlock.cs               |  2 +-
 .../Dataset/OneHotFeatureFlock.cs             |  2 +-
 .../Dataset/RepeatIntArray.cs                 |  2 +-
 .../Dataset/SegmentIntArray.cs                |  2 +-
 .../Dataset/SingletonFeatureFlock.cs          |  2 +-
 .../Dataset/SparseIntArray.cs                 |  2 +-
 src/Microsoft.ML.FastTree/FastTree.cs         |  1 -
 .../FastTreeClassification.cs                 |  1 -
 src/Microsoft.ML.FastTree/FastTreeRanking.cs  |  1 -
 .../FastTreeRegression.cs                     |  2 --
 src/Microsoft.ML.FastTree/FastTreeTweedie.cs  |  1 -
 .../GamClassification.cs                      |  1 -
 .../GamModelParameters.cs                     |  1 -
 src/Microsoft.ML.FastTree/GamRegression.cs    |  2 --
 src/Microsoft.ML.FastTree/GamTrainer.cs       |  3 ---
 src/Microsoft.ML.FastTree/RandomForest.cs     |  2 --
 .../RandomForestClassification.cs             |  1 -
 .../RandomForestRegression.cs                 |  1 -
 src/Microsoft.ML.FastTree/RegressionTree.cs   |  2 +-
 .../SumupPerformanceCommand.cs                |  3 +--
 .../Training/Applications/GradientWrappers.cs |  2 +-
 .../Applications/ObjectiveFunction.cs         |  2 +-
 .../Training/BaggingProvider.cs               |  2 +-
 .../Training/DcgCalculator.cs                 |  2 +-
 .../Training/DcgPermutationComparer.cs        |  2 +-
 .../Training/DocumentPartitioning.cs          |  2 +-
 .../IEnsembleCompressor.cs                    |  2 +-
 .../LassoBasedEnsembleCompressor.cs           |  2 +-
 .../Training/EnsembleCompression/LassoFit.cs  |  2 +-
 .../AcceleratedGradientDescent.cs             |  2 +-
 .../ConjugateGradientDescent.cs               |  2 +-
 .../OptimizationAlgorithms/GradientDescent.cs |  2 +-
 .../NoOptimizationAlgorithm.cs                |  2 +-
 .../OptimizationAlgorithm.cs                  |  2 +-
 .../Training/Parallel/IParallelTraining.cs    |  1 -
 .../Training/Parallel/SingleTrainer.cs        |  5 ++--
 .../Training/RegressionTreeNodeDocuments.cs   |  2 +-
 .../Training/ScoreTracker.cs                  |  2 +-
 .../Training/StepSearch.cs                    |  2 +-
 src/Microsoft.ML.FastTree/Training/Test.cs    |  2 +-
 .../FastForestLeastSquaresTreeLearner.cs      |  2 +-
 .../LeastSquaresRegressionTreeLearner.cs      |  2 +-
 .../Training/TreeLearners/TreeLearner.cs      |  2 +-
 .../Training/WinLossCalculator.cs             |  2 +-
 .../InternalQuantileRegressionTree.cs         |  2 +-
 .../TreeEnsemble/InternalRegressionTree.cs    |  2 +-
 .../TreeEnsemble/InternalTreeEnsemble.cs      |  2 +-
 .../TreeEnsemble/TreeEnsembleCombiner.cs      |  5 ++--
 src/Microsoft.ML.FastTree/Utils/Algorithms.cs |  2 +-
 .../Utils/BlockingThreadPool.cs               |  2 +-
 .../Utils/BufferPoolManager.cs                |  2 +-
 .../Utils/CompressUtils.cs                    |  2 +-
 .../Utils/FastTreeIniFileUtils.cs             |  2 +-
 .../Utils/LinqExtensions.cs                   |  2 +-
 src/Microsoft.ML.FastTree/Utils/MD5Hasher.cs  |  2 +-
 .../Utils/MappedObjectPool.cs                 |  2 +-
 .../Utils/PseudorandomFunction.cs             |  2 +-
 .../Utils/StreamExtensions.cs                 |  2 +-
 .../Utils/ThreadTaskManager.cs                |  2 +-
 src/Microsoft.ML.FastTree/Utils/Timer.cs      |  2 +-
 .../Utils/ToByteArrayExtensions.cs            |  2 +-
 .../Utils/VectorUtils.cs                      |  2 +-
 .../ComputeLRTrainingStdThroughHal.cs         |  2 +-
 .../HalLearnersCatalog.cs                     |  2 --
 .../OlsLinearRegression.cs                    |  1 -
 .../SymSgdClassificationTrainer.cs            |  5 ++--
 .../LightGbmBinaryTrainer.cs                  |  1 -
 .../LightGbmMulticlassTrainer.cs              |  2 +-
 .../LightGbmRankingTrainer.cs                 |  1 -
 .../LightGbmRegressionTrainer.cs              |  1 -
 .../LightGbmTrainerBase.cs                    |  2 +-
 .../WrappedLightGbmBooster.cs                 |  2 +-
 .../ResultProcessor.cs                        |  3 ++-
 .../Standard/LinearModelParameters.cs         |  4 +--
 .../Standard/LinearPredictorUtils.cs          |  2 +-
 .../LogisticRegression/LbfgsPredictorBase.cs  |  2 +-
 .../LogisticRegression/LogisticRegression.cs  |  4 +--
 .../MulticlassLogisticRegression.cs           |  3 +--
 .../Standard/ModelStatistics.cs               |  4 +--
 .../MultiClass/MetaMulticlassTrainer.cs       |  2 +-
 .../Standard/MultiClass/Ova.cs                |  1 -
 .../Standard/MultiClass/Pkpd.cs               |  1 -
 .../Standard/Online/AveragedLinear.cs         |  1 -
 .../Standard/Online/AveragedPerceptron.cs     |  1 -
 .../Standard/Online/LinearSvm.cs              |  1 -
 .../Standard/Online/OnlineGradientDescent.cs  |  1 -
 .../Standard/Online/OnlineLinear.cs           |  1 -
 .../PoissonRegression/PoissonRegression.cs    |  1 -
 .../Standard/SdcaBinary.cs                    |  1 -
 .../Standard/SdcaMultiClass.cs                |  1 -
 .../Standard/SdcaRegression.cs                |  1 -
 .../Standard/StochasticTrainerBase.cs         |  2 +-
 .../StandardLearnersCatalog.cs                | 19 +++++++-------
 src/Microsoft.ML.StaticPipe/LbfgsStatic.cs    | 19 +++++++-------
 .../OnlineLearnerStatic.cs                    |  2 +-
 .../SdcaStaticExtensions.cs                   |  1 -
 .../Algorithms/KdoSweeper.cs                  |  2 +-
 .../Algorithms/SmacSweeper.cs                 |  1 -
 src/Microsoft.ML.Sweeper/ConfigRunner.cs      |  2 +-
 .../SweepResultEvaluator.cs                   |  2 +-
 .../Common/EntryPoints/core_ep-list.tsv       | 26 +++++++++----------
 .../KMeansAndLogisticRegressionBench.cs       |  2 +-
 ...sticDualCoordinateAscentClassifierBench.cs |  1 -
 .../UnitTests/TestEntryPoints.cs              |  5 ++--
 .../UnitTests/TestUtilities.cs                |  1 -
 .../TestParallelFasttreeInterface.cs          |  2 +-
 .../TestPredictors.cs                         |  6 ++---
 .../Training.cs                               |  8 +++---
 .../BaseTestPredictorsMaml.cs                 |  2 +-
 .../EnvironmentExtensions.cs                  |  2 +-
 test/Microsoft.ML.Tests/OnnxConversionTest.cs |  2 +-
 .../PermutationFeatureImportanceTests.cs      |  2 +-
 .../Api/CookbookSamples/CookbookSamples.cs    |  1 -
 .../CookbookSamplesDynamicApi.cs              |  2 +-
 .../Api/Estimators/IntrospectiveTraining.cs   |  2 +-
 .../Api/Estimators/SimpleTrainAndPredict.cs   |  2 +-
 test/Microsoft.ML.Tests/Scenarios/OvaTest.cs  |  2 --
 .../TrainerEstimators/CalibratorEstimators.cs |  3 +--
 .../TrainerEstimators/LbfgsTests.cs           |  2 --
 .../SymSgdClassificationTests.cs              |  3 +--
 .../TreeEnsembleFeaturizerTest.cs             | 11 ++++----
 .../TrainerEstimators/TreeEstimators.cs       |  2 +-
 150 files changed, 159 insertions(+), 232 deletions(-)

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PFIHelper.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PFIHelper.cs
index 79052c2314..73471a0c52 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PFIHelper.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PFIHelper.cs
@@ -1,7 +1,8 @@
 using System;
 using System.Linq;
 using Microsoft.Data.DataView;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Data;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.SamplesUtils;
 using Microsoft.ML.Trainers.HalLearners;
 
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PfiBinaryClassificationExample.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PfiBinaryClassificationExample.cs
index 205f7cc4ce..77e2e63d27 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PfiBinaryClassificationExample.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/PermutationFeatureImportance/PfiBinaryClassificationExample.cs
@@ -1,6 +1,6 @@
 using System;
 using System.Linq;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Trainers;
 
 namespace Microsoft.ML.Samples.Dynamic.PermutationFeatureImportance
 {
diff --git a/docs/samples/Microsoft.ML.Samples/Static/SDCARegression.cs b/docs/samples/Microsoft.ML.Samples/Static/SDCARegression.cs
index 50d5b8c6aa..914788eae8 100644
--- a/docs/samples/Microsoft.ML.Samples/Static/SDCARegression.cs
+++ b/docs/samples/Microsoft.ML.Samples/Static/SDCARegression.cs
@@ -1,7 +1,7 @@
 using System;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.Trainers;
 
 namespace Microsoft.ML.Samples.Static
 {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
index cf88c501b8..baad8e2ca9 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
@@ -11,14 +11,13 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
 using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 
 [assembly: LoadableClass(typeof(void), typeof(EnsembleCreator), null, typeof(SignatureEntryPointModule), "CreateEnsemble")]
 
-namespace Microsoft.ML.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     /// <summary>
     /// A component to combine given models into an ensemble model.
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs b/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
index 83d8fd0a96..069cce2556 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.EntryPoints;
@@ -11,7 +11,7 @@
 [assembly: EntryPointModule(typeof(RegressionDisagreementDiversityFactory))]
 [assembly: EntryPointModule(typeof(MultiDisagreementDiversityFactory))]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     [TlcModule.Component(Name = DisagreementDiversityMeasure.LoadName, FriendlyName = DisagreementDiversityMeasure.UserName)]
     internal sealed class DisagreementDiversityFactory : ISupportBinaryDiversityMeasureFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
index 6cf8b418bc..4aa0ab10ce 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
@@ -3,12 +3,12 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
 
 [assembly: LoadableClass(typeof(void), typeof(Ensemble), null, typeof(SignatureEntryPointModule), "TrainEnsemble")]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     internal static class Ensemble
     {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs b/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
index c2d7862a31..66ae78ecb5 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.FeatureSelector;
 using Microsoft.ML.EntryPoints;
@@ -10,7 +10,7 @@
 [assembly: EntryPointModule(typeof(AllFeatureSelectorFactory))]
 [assembly: EntryPointModule(typeof(RandomFeatureSelector))]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     [TlcModule.Component(Name = AllFeatureSelector.LoadName, FriendlyName = AllFeatureSelector.UserName)]
     public sealed class AllFeatureSelectorFactory : ISupportFeatureSelectorFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs b/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
index 7583d72cfb..da10f30de4 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 
@@ -18,7 +18,7 @@
 [assembly: EntryPointModule(typeof(VotingFactory))]
 [assembly: EntryPointModule(typeof(WeightedAverage))]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     [TlcModule.Component(Name = Average.LoadName, FriendlyName = Average.UserName)]
     public sealed class AverageFactory : ISupportBinaryOutputCombinerFactory, ISupportRegressionOutputCombinerFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
index c80dba1738..9452a9d064 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
@@ -4,13 +4,13 @@
 
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Calibration;
 
 [assembly: EntryPointModule(typeof(PipelineEnsemble))]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     internal static class PipelineEnsemble
     {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs b/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
index 10b1551c62..b749501f76 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
@@ -16,7 +16,7 @@
 [assembly: EntryPointModule(typeof(BestPerformanceSelector))]
 [assembly: EntryPointModule(typeof(BestPerformanceSelectorMultiClass))]
 
-namespace Microsoft.ML.Ensemble.EntryPoints
+namespace Microsoft.ML.Ensemble
 {
     [TlcModule.Component(Name = AllSelector.LoadName, FriendlyName = AllSelector.UserName)]
     public sealed class AllSelectorFactory : ISupportBinarySubModelSelectorFactory, ISupportRegressionSubModelSelectorFactory
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
index e2a0147f01..3b2204af4a 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
@@ -7,7 +7,6 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.Ensemble.Selector.SubModelSelector;
@@ -19,8 +18,6 @@
 
 namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
 {
-    using TScalarPredictor = IPredictorProducing<Single>;
-
     internal sealed class BestDiverseSelectorBinary : BaseDiverseSelector<Single, DisagreementDiversityMeasure>, IBinarySubModelSelector
     {
         public const string UserName = "Best Diverse Selector";
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
index a25fe4d8d1..8bae59d1f5 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
@@ -8,7 +8,6 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.Ensemble.Selector.SubModelSelector;
@@ -20,8 +19,6 @@
 
 namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
 {
-    using TVectorPredictor = IPredictorProducing<VBuffer<Single>>;
-
     internal sealed class BestDiverseSelectorMultiClass : BaseDiverseSelector<VBuffer<Single>, IDiversityMeasure<VBuffer<Single>>>, IMulticlassSubModelSelector
     {
         public const string UserName = "Best Diverse Selector";
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
index ef310eab7e..132f691034 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
@@ -7,7 +7,6 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.Ensemble.Selector.SubModelSelector;
@@ -19,8 +18,6 @@
 
 namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
 {
-    using TScalarPredictor = IPredictorProducing<Single>;
-
     internal sealed class BestDiverseSelectorRegression : BaseDiverseSelector<Single, RegressionDisagreementDiversityMeasure>, IRegressionSubModelSelector
     {
         public const string UserName = "Best Diverse Selector";
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
index a17ea20d88..8504525012 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
@@ -6,7 +6,6 @@
 using System.Collections.Generic;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Transforms;
 
 namespace Microsoft.ML.Ensemble.Selector.SubsetSelector
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
index 83637c333f..d63a183f2c 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
@@ -8,11 +8,10 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
 
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
index d194dfc947..9f5446d327 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
@@ -9,11 +9,10 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
 using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(MulticlassDataPartitionEnsembleTrainer.Summary, typeof(MulticlassDataPartitionEnsembleTrainer),
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
index 569d4cd829..a4075c9c94 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
@@ -7,13 +7,11 @@
 using System.Linq;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Data;
 using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.EntryPoints;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
 
diff --git a/src/Microsoft.ML.FastTree/BinFile/BinFinder.cs b/src/Microsoft.ML.FastTree/BinFile/BinFinder.cs
index 7725cb7aeb..7df57005b7 100644
--- a/src/Microsoft.ML.FastTree/BinFile/BinFinder.cs
+++ b/src/Microsoft.ML.FastTree/BinFile/BinFinder.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// A class that bins vectors of doubles into a specified number of equal mass bins.
diff --git a/src/Microsoft.ML.FastTree/BinFile/IniFileParserInterface.cs b/src/Microsoft.ML.FastTree/BinFile/IniFileParserInterface.cs
index 9b28df134c..257b9e83b5 100644
--- a/src/Microsoft.ML.FastTree/BinFile/IniFileParserInterface.cs
+++ b/src/Microsoft.ML.FastTree/BinFile/IniFileParserInterface.cs
@@ -7,7 +7,7 @@
 using System.Runtime.InteropServices;
 using System.Text;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal sealed class IniFileParserInterface
     {
diff --git a/src/Microsoft.ML.FastTree/BoostingFastTree.cs b/src/Microsoft.ML.FastTree/BoostingFastTree.cs
index e84ec0117b..072ee2e20f 100644
--- a/src/Microsoft.ML.FastTree/BoostingFastTree.cs
+++ b/src/Microsoft.ML.FastTree/BoostingFastTree.cs
@@ -6,7 +6,6 @@
 using System.Linq;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Float = System.Single;
 
 namespace Microsoft.ML.Trainers.FastTree
diff --git a/src/Microsoft.ML.FastTree/Dataset/Dataset.cs b/src/Microsoft.ML.FastTree/Dataset/Dataset.cs
index 330ef028ca..1e5de9b229 100644
--- a/src/Microsoft.ML.FastTree/Dataset/Dataset.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/Dataset.cs
@@ -8,7 +8,7 @@
 using System.Threading.Tasks;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// A dataset of features.
diff --git a/src/Microsoft.ML.FastTree/Dataset/DatasetUtils.cs b/src/Microsoft.ML.FastTree/Dataset/DatasetUtils.cs
index 542b097381..617e6559e0 100644
--- a/src/Microsoft.ML.FastTree/Dataset/DatasetUtils.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/DatasetUtils.cs
@@ -5,7 +5,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// Loads training/validation/test sets from file
diff --git a/src/Microsoft.ML.FastTree/Dataset/DenseIntArray.cs b/src/Microsoft.ML.FastTree/Dataset/DenseIntArray.cs
index c6f10843d8..68c33a0d2d 100644
--- a/src/Microsoft.ML.FastTree/Dataset/DenseIntArray.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/DenseIntArray.cs
@@ -8,7 +8,7 @@
 using System.Runtime.InteropServices;
 using System.Security;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Dataset/Feature.cs b/src/Microsoft.ML.FastTree/Dataset/Feature.cs
index c74ef5baf4..3013b536cf 100644
--- a/src/Microsoft.ML.FastTree/Dataset/Feature.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/Feature.cs
@@ -4,7 +4,7 @@
 
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 
     /// <summary>
diff --git a/src/Microsoft.ML.FastTree/Dataset/FeatureFlock.cs b/src/Microsoft.ML.FastTree/Dataset/FeatureFlock.cs
index 73c6b646bf..2d370499a4 100644
--- a/src/Microsoft.ML.FastTree/Dataset/FeatureFlock.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/FeatureFlock.cs
@@ -15,7 +15,7 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// Holds statistics per bin value for a feature. These are yielded by <see cref="SufficientStatsBase.GetBinStats"/>
diff --git a/src/Microsoft.ML.FastTree/Dataset/FeatureHistogram.cs b/src/Microsoft.ML.FastTree/Dataset/FeatureHistogram.cs
index 2294525a9f..4968ad6a58 100644
--- a/src/Microsoft.ML.FastTree/Dataset/FeatureHistogram.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/FeatureHistogram.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Dataset/FileObjectStore.cs b/src/Microsoft.ML.FastTree/Dataset/FileObjectStore.cs
index 9f7623d0ad..ff6e990dca 100644
--- a/src/Microsoft.ML.FastTree/Dataset/FileObjectStore.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/FileObjectStore.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if !NO_STORE
     /// <summary>
diff --git a/src/Microsoft.ML.FastTree/Dataset/IntArray.cs b/src/Microsoft.ML.FastTree/Dataset/IntArray.cs
index e2c8de872b..05ed42c957 100644
--- a/src/Microsoft.ML.FastTree/Dataset/IntArray.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/IntArray.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Dataset/NHotFeatureFlock.cs b/src/Microsoft.ML.FastTree/Dataset/NHotFeatureFlock.cs
index fcfa6c938a..43eacf22f0 100644
--- a/src/Microsoft.ML.FastTree/Dataset/NHotFeatureFlock.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/NHotFeatureFlock.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This is a feature flock that misuses a property of <see cref="DeltaSparseIntArray.Sumup"/>
diff --git a/src/Microsoft.ML.FastTree/Dataset/OneHotFeatureFlock.cs b/src/Microsoft.ML.FastTree/Dataset/OneHotFeatureFlock.cs
index da8592fc14..605d912328 100644
--- a/src/Microsoft.ML.FastTree/Dataset/OneHotFeatureFlock.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/OneHotFeatureFlock.cs
@@ -4,7 +4,7 @@
 
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// A feature flock for a set of features where per example at most one of the features has a
diff --git a/src/Microsoft.ML.FastTree/Dataset/RepeatIntArray.cs b/src/Microsoft.ML.FastTree/Dataset/RepeatIntArray.cs
index 3b9502a8db..ac176c20ec 100644
--- a/src/Microsoft.ML.FastTree/Dataset/RepeatIntArray.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/RepeatIntArray.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = Single;
diff --git a/src/Microsoft.ML.FastTree/Dataset/SegmentIntArray.cs b/src/Microsoft.ML.FastTree/Dataset/SegmentIntArray.cs
index 156f5e227c..bfeb19f2f2 100644
--- a/src/Microsoft.ML.FastTree/Dataset/SegmentIntArray.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/SegmentIntArray.cs
@@ -7,7 +7,7 @@
 using System.Runtime.InteropServices;
 using System.Security;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Dataset/SingletonFeatureFlock.cs b/src/Microsoft.ML.FastTree/Dataset/SingletonFeatureFlock.cs
index c1cf5fe4d4..30f609faa4 100644
--- a/src/Microsoft.ML.FastTree/Dataset/SingletonFeatureFlock.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/SingletonFeatureFlock.cs
@@ -5,7 +5,7 @@
 using System.Linq;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// The singleton feature flock is the simplest possible sort of flock, that is, a flock
diff --git a/src/Microsoft.ML.FastTree/Dataset/SparseIntArray.cs b/src/Microsoft.ML.FastTree/Dataset/SparseIntArray.cs
index b3dc61b07a..b9e5cc0f28 100644
--- a/src/Microsoft.ML.FastTree/Dataset/SparseIntArray.cs
+++ b/src/Microsoft.ML.FastTree/Dataset/SparseIntArray.cs
@@ -7,7 +7,7 @@
 using System.Runtime.InteropServices;
 using System.Security;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/FastTree.cs b/src/Microsoft.ML.FastTree/FastTree.cs
index fc94e6099c..c2a90c4f3e 100644
--- a/src/Microsoft.ML.FastTree/FastTree.cs
+++ b/src/Microsoft.ML.FastTree/FastTree.cs
@@ -23,7 +23,6 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.Model.Pfa;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.Conversions;
diff --git a/src/Microsoft.ML.FastTree/FastTreeClassification.cs b/src/Microsoft.ML.FastTree/FastTreeClassification.cs
index ff47449238..4ae7ab7555 100644
--- a/src/Microsoft.ML.FastTree/FastTreeClassification.cs
+++ b/src/Microsoft.ML.FastTree/FastTreeClassification.cs
@@ -15,7 +15,6 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(FastTreeBinaryClassificationTrainer.Summary, typeof(FastTreeBinaryClassificationTrainer), typeof(FastTreeBinaryClassificationTrainer.Options),
diff --git a/src/Microsoft.ML.FastTree/FastTreeRanking.cs b/src/Microsoft.ML.FastTree/FastTreeRanking.cs
index 485a7639ca..059cd8ab93 100644
--- a/src/Microsoft.ML.FastTree/FastTreeRanking.cs
+++ b/src/Microsoft.ML.FastTree/FastTreeRanking.cs
@@ -17,7 +17,6 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 // REVIEW: Do we really need all these names?
diff --git a/src/Microsoft.ML.FastTree/FastTreeRegression.cs b/src/Microsoft.ML.FastTree/FastTreeRegression.cs
index e26ce00e82..1399b8bebe 100644
--- a/src/Microsoft.ML.FastTree/FastTreeRegression.cs
+++ b/src/Microsoft.ML.FastTree/FastTreeRegression.cs
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
 using System.Linq;
 using System.Text;
 using Microsoft.Data.DataView;
@@ -13,7 +12,6 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(FastTreeRegressionTrainer.Summary, typeof(FastTreeRegressionTrainer), typeof(FastTreeRegressionTrainer.Options),
diff --git a/src/Microsoft.ML.FastTree/FastTreeTweedie.cs b/src/Microsoft.ML.FastTree/FastTreeTweedie.cs
index b8ea0ce030..547d48f7ca 100644
--- a/src/Microsoft.ML.FastTree/FastTreeTweedie.cs
+++ b/src/Microsoft.ML.FastTree/FastTreeTweedie.cs
@@ -14,7 +14,6 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(FastTreeTweedieTrainer.Summary, typeof(FastTreeTweedieTrainer), typeof(FastTreeTweedieTrainer.Options),
diff --git a/src/Microsoft.ML.FastTree/GamClassification.cs b/src/Microsoft.ML.FastTree/GamClassification.cs
index 635862e178..79f8265dbb 100644
--- a/src/Microsoft.ML.FastTree/GamClassification.cs
+++ b/src/Microsoft.ML.FastTree/GamClassification.cs
@@ -14,7 +14,6 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(BinaryClassificationGamTrainer.Summary,
diff --git a/src/Microsoft.ML.FastTree/GamModelParameters.cs b/src/Microsoft.ML.FastTree/GamModelParameters.cs
index 38c931f03f..a726edd1bc 100644
--- a/src/Microsoft.ML.FastTree/GamModelParameters.cs
+++ b/src/Microsoft.ML.FastTree/GamModelParameters.cs
@@ -18,7 +18,6 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(typeof(GamModelParametersBase.VisualizationCommand), typeof(GamModelParametersBase.VisualizationCommand.Arguments), typeof(SignatureCommand),
diff --git a/src/Microsoft.ML.FastTree/GamRegression.cs b/src/Microsoft.ML.FastTree/GamRegression.cs
index 100cc6b61a..4c3fbe021b 100644
--- a/src/Microsoft.ML.FastTree/GamRegression.cs
+++ b/src/Microsoft.ML.FastTree/GamRegression.cs
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
 using Microsoft.Data.DataView;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
@@ -11,7 +10,6 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(RegressionGamTrainer.Summary,
diff --git a/src/Microsoft.ML.FastTree/GamTrainer.cs b/src/Microsoft.ML.FastTree/GamTrainer.cs
index 87bcc370ba..11953bbe36 100644
--- a/src/Microsoft.ML.FastTree/GamTrainer.cs
+++ b/src/Microsoft.ML.FastTree/GamTrainer.cs
@@ -14,15 +14,12 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
-using Timer = Microsoft.ML.Trainers.FastTree.Internal.Timer;
 
 [assembly: LoadableClass(typeof(void), typeof(Gam), null, typeof(SignatureEntryPointModule), "GAM")]
 
 namespace Microsoft.ML.Trainers.FastTree
 {
-    using AutoResetEvent = System.Threading.AutoResetEvent;
     using SplitInfo = LeastSquaresRegressionTreeLearner.SplitInfo;
 
     /// <summary>
diff --git a/src/Microsoft.ML.FastTree/RandomForest.cs b/src/Microsoft.ML.FastTree/RandomForest.cs
index 943b10f621..aea3a991bf 100644
--- a/src/Microsoft.ML.FastTree/RandomForest.cs
+++ b/src/Microsoft.ML.FastTree/RandomForest.cs
@@ -2,9 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
 using Microsoft.ML.Core.Data;
-using Microsoft.ML.Trainers.FastTree.Internal;
 
 namespace Microsoft.ML.Trainers.FastTree
 {
diff --git a/src/Microsoft.ML.FastTree/RandomForestClassification.cs b/src/Microsoft.ML.FastTree/RandomForestClassification.cs
index d6a31ec6a0..13d2261ea9 100644
--- a/src/Microsoft.ML.FastTree/RandomForestClassification.cs
+++ b/src/Microsoft.ML.FastTree/RandomForestClassification.cs
@@ -15,7 +15,6 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(FastForestClassification.Summary, typeof(FastForestClassification), typeof(FastForestClassification.Options),
diff --git a/src/Microsoft.ML.FastTree/RandomForestRegression.cs b/src/Microsoft.ML.FastTree/RandomForestRegression.cs
index de83cabeca..daed4b26c1 100644
--- a/src/Microsoft.ML.FastTree/RandomForestRegression.cs
+++ b/src/Microsoft.ML.FastTree/RandomForestRegression.cs
@@ -13,7 +13,6 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(FastForestRegression.Summary, typeof(FastForestRegression), typeof(FastForestRegression.Options),
diff --git a/src/Microsoft.ML.FastTree/RegressionTree.cs b/src/Microsoft.ML.FastTree/RegressionTree.cs
index 867d6015ef..96a0784602 100644
--- a/src/Microsoft.ML.FastTree/RegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/RegressionTree.cs
@@ -4,7 +4,7 @@
 
 using System.Collections.Generic;
 using System.Collections.Immutable;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 
 namespace Microsoft.ML.FastTree
 {
diff --git a/src/Microsoft.ML.FastTree/SumupPerformanceCommand.cs b/src/Microsoft.ML.FastTree/SumupPerformanceCommand.cs
index a25b370586..7816dc993b 100644
--- a/src/Microsoft.ML.FastTree/SumupPerformanceCommand.cs
+++ b/src/Microsoft.ML.FastTree/SumupPerformanceCommand.cs
@@ -17,14 +17,13 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 
 [assembly: LoadableClass(typeof(SumupPerformanceCommand), typeof(SumupPerformanceCommand.Arguments), typeof(SignatureCommand),
     "", "FastTreeSumupPerformance", "ftsumup")]
 
 namespace Microsoft.ML.Trainers.FastTree
 {
-using Stopwatch = System.Diagnostics.Stopwatch;
+    using Stopwatch = System.Diagnostics.Stopwatch;
 
     /// <summary>
     /// This is an internal utility command to measure the performance of the IntArray sumup operation.
diff --git a/src/Microsoft.ML.FastTree/Training/Applications/GradientWrappers.cs b/src/Microsoft.ML.FastTree/Training/Applications/GradientWrappers.cs
index a1e65a455b..b360b9ea4a 100644
--- a/src/Microsoft.ML.FastTree/Training/Applications/GradientWrappers.cs
+++ b/src/Microsoft.ML.FastTree/Training/Applications/GradientWrappers.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// Trivial weights wrapper. Creates proxy class holding the targets.
diff --git a/src/Microsoft.ML.FastTree/Training/Applications/ObjectiveFunction.cs b/src/Microsoft.ML.FastTree/Training/Applications/ObjectiveFunction.cs
index 2808e24cb7..644add5903 100644
--- a/src/Microsoft.ML.FastTree/Training/Applications/ObjectiveFunction.cs
+++ b/src/Microsoft.ML.FastTree/Training/Applications/ObjectiveFunction.cs
@@ -7,7 +7,7 @@
 using System.Linq;
 using System.Threading.Tasks;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public abstract class ObjectiveFunctionBase
     {
diff --git a/src/Microsoft.ML.FastTree/Training/BaggingProvider.cs b/src/Microsoft.ML.FastTree/Training/BaggingProvider.cs
index 3fd5125620..0dc50ce83b 100644
--- a/src/Microsoft.ML.FastTree/Training/BaggingProvider.cs
+++ b/src/Microsoft.ML.FastTree/Training/BaggingProvider.cs
@@ -4,7 +4,7 @@
 
 using System;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public class BaggingProvider
     {
diff --git a/src/Microsoft.ML.FastTree/Training/DcgCalculator.cs b/src/Microsoft.ML.FastTree/Training/DcgCalculator.cs
index e9284a6a5e..7fd960a190 100644
--- a/src/Microsoft.ML.FastTree/Training/DcgCalculator.cs
+++ b/src/Microsoft.ML.FastTree/Training/DcgCalculator.cs
@@ -7,7 +7,7 @@
 using System.Threading.Tasks;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public sealed class DcgCalculator
     {
diff --git a/src/Microsoft.ML.FastTree/Training/DcgPermutationComparer.cs b/src/Microsoft.ML.FastTree/Training/DcgPermutationComparer.cs
index 05e2955ec9..e0fb3ff926 100644
--- a/src/Microsoft.ML.FastTree/Training/DcgPermutationComparer.cs
+++ b/src/Microsoft.ML.FastTree/Training/DcgPermutationComparer.cs
@@ -4,7 +4,7 @@
 
 using System.Collections.Generic;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public abstract class DcgPermutationComparer : IComparer<int>
     {
diff --git a/src/Microsoft.ML.FastTree/Training/DocumentPartitioning.cs b/src/Microsoft.ML.FastTree/Training/DocumentPartitioning.cs
index 3a52acdebf..6e06480014 100644
--- a/src/Microsoft.ML.FastTree/Training/DocumentPartitioning.cs
+++ b/src/Microsoft.ML.FastTree/Training/DocumentPartitioning.cs
@@ -7,7 +7,7 @@
 using System.Linq;
 using System.Threading.Tasks;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/IEnsembleCompressor.cs b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/IEnsembleCompressor.cs
index 7f3c687819..fb87ab95f2 100644
--- a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/IEnsembleCompressor.cs
+++ b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/IEnsembleCompressor.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal interface IEnsembleCompressor<TLabel>
     {
diff --git a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoBasedEnsembleCompressor.cs b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoBasedEnsembleCompressor.cs
index eba82d0cfe..69b2acd798 100644
--- a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoBasedEnsembleCompressor.cs
+++ b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoBasedEnsembleCompressor.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This implementation is based on:
diff --git a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoFit.cs b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoFit.cs
index 7a1e7ac321..a29752f612 100644
--- a/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoFit.cs
+++ b/src/Microsoft.ML.FastTree/Training/EnsembleCompression/LassoFit.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public sealed class LassoFit
     {
diff --git a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/AcceleratedGradientDescent.cs b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/AcceleratedGradientDescent.cs
index 0678eb4060..72cf5af52c 100644
--- a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/AcceleratedGradientDescent.cs
+++ b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/AcceleratedGradientDescent.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     //Accelerated gradient descent score tracker
     internal class AcceleratedGradientDescent : GradientDescent
diff --git a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/ConjugateGradientDescent.cs b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/ConjugateGradientDescent.cs
index 044ef7bcf3..bde9917c53 100644
--- a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/ConjugateGradientDescent.cs
+++ b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/ConjugateGradientDescent.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     // Conjugate gradient descent
     internal class ConjugateGradientDescent : GradientDescent
diff --git a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/GradientDescent.cs b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/GradientDescent.cs
index b2cf4f584b..5543763351 100644
--- a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/GradientDescent.cs
+++ b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/GradientDescent.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal class GradientDescent : OptimizationAlgorithm
     {
diff --git a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/NoOptimizationAlgorithm.cs b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/NoOptimizationAlgorithm.cs
index 1925272122..9e53353316 100644
--- a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/NoOptimizationAlgorithm.cs
+++ b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/NoOptimizationAlgorithm.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This is dummy optimizer. As Random forest does not have any boosting based optimization, this is place holder to be consistent
diff --git a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/OptimizationAlgorithm.cs b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/OptimizationAlgorithm.cs
index 299d13a300..986a9c13f7 100644
--- a/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/OptimizationAlgorithm.cs
+++ b/src/Microsoft.ML.FastTree/Training/OptimizationAlgorithms/OptimizationAlgorithm.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Collections.Generic;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     //An interface that can be implemnted on
     public interface IFastTrainingScoresUpdate
diff --git a/src/Microsoft.ML.FastTree/Training/Parallel/IParallelTraining.cs b/src/Microsoft.ML.FastTree/Training/Parallel/IParallelTraining.cs
index fccb0673eb..965026556c 100644
--- a/src/Microsoft.ML.FastTree/Training/Parallel/IParallelTraining.cs
+++ b/src/Microsoft.ML.FastTree/Training/Parallel/IParallelTraining.cs
@@ -4,7 +4,6 @@
 
 using System;
 using Microsoft.ML.EntryPoints;
-using Microsoft.ML.Trainers.FastTree.Internal;
 
 namespace Microsoft.ML.Trainers.FastTree
 {
diff --git a/src/Microsoft.ML.FastTree/Training/Parallel/SingleTrainer.cs b/src/Microsoft.ML.FastTree/Training/Parallel/SingleTrainer.cs
index 734cda69d9..52adef9a50 100644
--- a/src/Microsoft.ML.FastTree/Training/Parallel/SingleTrainer.cs
+++ b/src/Microsoft.ML.FastTree/Training/Parallel/SingleTrainer.cs
@@ -15,9 +15,8 @@
 
 namespace Microsoft.ML.Trainers.FastTree
 {
-    using Microsoft.ML.Trainers.FastTree.Internal;
-    using LeafSplitCandidates = Internal.LeastSquaresRegressionTreeLearner.LeafSplitCandidates;
-    using SplitInfo = Internal.LeastSquaresRegressionTreeLearner.SplitInfo;
+    using LeafSplitCandidates = LeastSquaresRegressionTreeLearner.LeafSplitCandidates;
+    using SplitInfo = LeastSquaresRegressionTreeLearner.SplitInfo;
 
     internal sealed class SingleTrainer : IParallelTraining
     {
diff --git a/src/Microsoft.ML.FastTree/Training/RegressionTreeNodeDocuments.cs b/src/Microsoft.ML.FastTree/Training/RegressionTreeNodeDocuments.cs
index 75fe766cc6..8849aaa53e 100644
--- a/src/Microsoft.ML.FastTree/Training/RegressionTreeNodeDocuments.cs
+++ b/src/Microsoft.ML.FastTree/Training/RegressionTreeNodeDocuments.cs
@@ -5,7 +5,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     // RegressionTreeNodeDocuments represents an association between a node in a regression
     // tree and documents belonging to that node.
diff --git a/src/Microsoft.ML.FastTree/Training/ScoreTracker.cs b/src/Microsoft.ML.FastTree/Training/ScoreTracker.cs
index 297aa06e29..882fa4a2d6 100644
--- a/src/Microsoft.ML.FastTree/Training/ScoreTracker.cs
+++ b/src/Microsoft.ML.FastTree/Training/ScoreTracker.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Threading.Tasks;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public class ScoreTracker
     {
diff --git a/src/Microsoft.ML.FastTree/Training/StepSearch.cs b/src/Microsoft.ML.FastTree/Training/StepSearch.cs
index bffa5213ca..ca767a22ed 100644
--- a/src/Microsoft.ML.FastTree/Training/StepSearch.cs
+++ b/src/Microsoft.ML.FastTree/Training/StepSearch.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal interface IStepSearch
     {
diff --git a/src/Microsoft.ML.FastTree/Training/Test.cs b/src/Microsoft.ML.FastTree/Training/Test.cs
index c76903d522..88f985f67a 100644
--- a/src/Microsoft.ML.FastTree/Training/Test.cs
+++ b/src/Microsoft.ML.FastTree/Training/Test.cs
@@ -8,7 +8,7 @@
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public sealed class TestResult : IComparable<TestResult>
     {
diff --git a/src/Microsoft.ML.FastTree/Training/TreeLearners/FastForestLeastSquaresTreeLearner.cs b/src/Microsoft.ML.FastTree/Training/TreeLearners/FastForestLeastSquaresTreeLearner.cs
index 3009bd500a..ae7f2a3b2f 100644
--- a/src/Microsoft.ML.FastTree/Training/TreeLearners/FastForestLeastSquaresTreeLearner.cs
+++ b/src/Microsoft.ML.FastTree/Training/TreeLearners/FastForestLeastSquaresTreeLearner.cs
@@ -4,7 +4,7 @@
 
 using System;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal class RandomForestLeastSquaresTreeLearner : LeastSquaresRegressionTreeLearner
     {
diff --git a/src/Microsoft.ML.FastTree/Training/TreeLearners/LeastSquaresRegressionTreeLearner.cs b/src/Microsoft.ML.FastTree/Training/TreeLearners/LeastSquaresRegressionTreeLearner.cs
index 823942f824..2cf8f44a75 100644
--- a/src/Microsoft.ML.FastTree/Training/TreeLearners/LeastSquaresRegressionTreeLearner.cs
+++ b/src/Microsoft.ML.FastTree/Training/TreeLearners/LeastSquaresRegressionTreeLearner.cs
@@ -9,7 +9,7 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
 #if USE_SINGLE_PRECISION
     using FloatType = System.Single;
diff --git a/src/Microsoft.ML.FastTree/Training/TreeLearners/TreeLearner.cs b/src/Microsoft.ML.FastTree/Training/TreeLearners/TreeLearner.cs
index e5f633b9c9..93dd45ca60 100644
--- a/src/Microsoft.ML.FastTree/Training/TreeLearners/TreeLearner.cs
+++ b/src/Microsoft.ML.FastTree/Training/TreeLearners/TreeLearner.cs
@@ -4,7 +4,7 @@
 
 using System;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal abstract class TreeLearner
     {
diff --git a/src/Microsoft.ML.FastTree/Training/WinLossCalculator.cs b/src/Microsoft.ML.FastTree/Training/WinLossCalculator.cs
index 0320a23ded..a183ddc9f6 100644
--- a/src/Microsoft.ML.FastTree/Training/WinLossCalculator.cs
+++ b/src/Microsoft.ML.FastTree/Training/WinLossCalculator.cs
@@ -8,7 +8,7 @@
 using System.Threading.Tasks;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public sealed class WinLossCalculator
     {
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
index 453a223449..7a8d5dcf23 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Model;
 using Float = System.Single;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal class InternalQuantileRegressionTree : InternalRegressionTree
     {
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
index 8feb116f1d..31f65ae92e 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
@@ -15,7 +15,7 @@
 using Microsoft.ML.Model.Pfa;
 using Newtonsoft.Json.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// Note that <see cref="InternalRegressionTree"/> is shared between FastTree and LightGBM assemblies,
     /// so <see cref="InternalRegressionTree"/> has <see cref="BestFriendAttribute"/>.
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
index 3a7a49573f..62fd670fc8 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalTreeEnsemble.cs
@@ -14,7 +14,7 @@
 using Microsoft.ML.Model.Pfa;
 using Newtonsoft.Json.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     [BestFriend]
     internal class InternalTreeEnsemble
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
index f215dec82a..48819147ef 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
@@ -8,12 +8,11 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Ensemble;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 
 [assembly: LoadableClass(typeof(TreeEnsembleCombiner), null, typeof(SignatureModelCombiner), "Fast Tree Model Combiner", "FastTreeCombiner")]
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public sealed class TreeEnsembleCombiner : IModelCombiner
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/Algorithms.cs b/src/Microsoft.ML.FastTree/Utils/Algorithms.cs
index 9ad94b359a..553e0e7553 100644
--- a/src/Microsoft.ML.FastTree/Utils/Algorithms.cs
+++ b/src/Microsoft.ML.FastTree/Utils/Algorithms.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public static class Algorithms
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/BlockingThreadPool.cs b/src/Microsoft.ML.FastTree/Utils/BlockingThreadPool.cs
index 76d623a850..e096de7005 100644
--- a/src/Microsoft.ML.FastTree/Utils/BlockingThreadPool.cs
+++ b/src/Microsoft.ML.FastTree/Utils/BlockingThreadPool.cs
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This class wraps the standard .NET ThreadPool and adds the following functionality:
diff --git a/src/Microsoft.ML.FastTree/Utils/BufferPoolManager.cs b/src/Microsoft.ML.FastTree/Utils/BufferPoolManager.cs
index cd0d03cfc8..526fddba09 100644
--- a/src/Microsoft.ML.FastTree/Utils/BufferPoolManager.cs
+++ b/src/Microsoft.ML.FastTree/Utils/BufferPoolManager.cs
@@ -10,7 +10,7 @@
 using System.Linq;
 using System.Runtime.InteropServices;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This class enables basic buffer pooling.
diff --git a/src/Microsoft.ML.FastTree/Utils/CompressUtils.cs b/src/Microsoft.ML.FastTree/Utils/CompressUtils.cs
index 0588e386ae..4c87a666a9 100644
--- a/src/Microsoft.ML.FastTree/Utils/CompressUtils.cs
+++ b/src/Microsoft.ML.FastTree/Utils/CompressUtils.cs
@@ -7,7 +7,7 @@
 using System.IO;
 using System.IO.Compression;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal struct BufferBlock
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/FastTreeIniFileUtils.cs b/src/Microsoft.ML.FastTree/Utils/FastTreeIniFileUtils.cs
index 939306cbaa..a9400739dd 100644
--- a/src/Microsoft.ML.FastTree/Utils/FastTreeIniFileUtils.cs
+++ b/src/Microsoft.ML.FastTree/Utils/FastTreeIniFileUtils.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal static class FastTreeIniFileUtils
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/LinqExtensions.cs b/src/Microsoft.ML.FastTree/Utils/LinqExtensions.cs
index 88d8825205..0b912e58bc 100644
--- a/src/Microsoft.ML.FastTree/Utils/LinqExtensions.cs
+++ b/src/Microsoft.ML.FastTree/Utils/LinqExtensions.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public static class LinqExtensions
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/MD5Hasher.cs b/src/Microsoft.ML.FastTree/Utils/MD5Hasher.cs
index fc31ec097e..8a5fda81ea 100644
--- a/src/Microsoft.ML.FastTree/Utils/MD5Hasher.cs
+++ b/src/Microsoft.ML.FastTree/Utils/MD5Hasher.cs
@@ -7,7 +7,7 @@
 using System.Security.Cryptography;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public struct MD5Hash
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/MappedObjectPool.cs b/src/Microsoft.ML.FastTree/Utils/MappedObjectPool.cs
index bcfb68e210..ad9b54e6cd 100644
--- a/src/Microsoft.ML.FastTree/Utils/MappedObjectPool.cs
+++ b/src/Microsoft.ML.FastTree/Utils/MappedObjectPool.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// Implements a paging mechanism on indexed objects.
diff --git a/src/Microsoft.ML.FastTree/Utils/PseudorandomFunction.cs b/src/Microsoft.ML.FastTree/Utils/PseudorandomFunction.cs
index 59adf3a453..9a7800d5ac 100644
--- a/src/Microsoft.ML.FastTree/Utils/PseudorandomFunction.cs
+++ b/src/Microsoft.ML.FastTree/Utils/PseudorandomFunction.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Linq;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This class defines a psuedorandom function, mapping a number to
diff --git a/src/Microsoft.ML.FastTree/Utils/StreamExtensions.cs b/src/Microsoft.ML.FastTree/Utils/StreamExtensions.cs
index c12ba5d066..125b49fecb 100644
--- a/src/Microsoft.ML.FastTree/Utils/StreamExtensions.cs
+++ b/src/Microsoft.ML.FastTree/Utils/StreamExtensions.cs
@@ -5,7 +5,7 @@
 using System.IO;
 using System.IO.Compression;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public static class StreamExtensions
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/ThreadTaskManager.cs b/src/Microsoft.ML.FastTree/Utils/ThreadTaskManager.cs
index d479c2c53b..aa0e06ffc0 100644
--- a/src/Microsoft.ML.FastTree/Utils/ThreadTaskManager.cs
+++ b/src/Microsoft.ML.FastTree/Utils/ThreadTaskManager.cs
@@ -7,7 +7,7 @@
 using System.Linq;
 using System.Threading.Tasks;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     internal static class ThreadTaskManager
     {
diff --git a/src/Microsoft.ML.FastTree/Utils/Timer.cs b/src/Microsoft.ML.FastTree/Utils/Timer.cs
index 35770a5e50..feb8d631cc 100644
--- a/src/Microsoft.ML.FastTree/Utils/Timer.cs
+++ b/src/Microsoft.ML.FastTree/Utils/Timer.cs
@@ -6,7 +6,7 @@
 using System.Text;
 using System.Threading;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     using Stopwatch = System.Diagnostics.Stopwatch;
 
diff --git a/src/Microsoft.ML.FastTree/Utils/ToByteArrayExtensions.cs b/src/Microsoft.ML.FastTree/Utils/ToByteArrayExtensions.cs
index 108aeeac87..1452a3bbba 100644
--- a/src/Microsoft.ML.FastTree/Utils/ToByteArrayExtensions.cs
+++ b/src/Microsoft.ML.FastTree/Utils/ToByteArrayExtensions.cs
@@ -7,7 +7,7 @@
 using System.Text;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     /// <summary>
     /// This class contains extension methods that support binary serialization of some base C# types
diff --git a/src/Microsoft.ML.FastTree/Utils/VectorUtils.cs b/src/Microsoft.ML.FastTree/Utils/VectorUtils.cs
index 8941d50da0..3b54b1a33b 100644
--- a/src/Microsoft.ML.FastTree/Utils/VectorUtils.cs
+++ b/src/Microsoft.ML.FastTree/Utils/VectorUtils.cs
@@ -5,7 +5,7 @@
 using System;
 using System.Text;
 
-namespace Microsoft.ML.Trainers.FastTree.Internal
+namespace Microsoft.ML.Trainers.FastTree
 {
     public class VectorUtils
     {
diff --git a/src/Microsoft.ML.HalLearners/ComputeLRTrainingStdThroughHal.cs b/src/Microsoft.ML.HalLearners/ComputeLRTrainingStdThroughHal.cs
index bc30deabfc..ce3bad8b99 100644
--- a/src/Microsoft.ML.HalLearners/ComputeLRTrainingStdThroughHal.cs
+++ b/src/Microsoft.ML.HalLearners/ComputeLRTrainingStdThroughHal.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Trainers.HalLearners;
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     using Mkl = OlsLinearRegressionTrainer.Mkl;
 
diff --git a/src/Microsoft.ML.HalLearners/HalLearnersCatalog.cs b/src/Microsoft.ML.HalLearners/HalLearnersCatalog.cs
index 1b635d49fc..564a5df3c6 100644
--- a/src/Microsoft.ML.HalLearners/HalLearnersCatalog.cs
+++ b/src/Microsoft.ML.HalLearners/HalLearnersCatalog.cs
@@ -2,11 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Trainers.HalLearners;
-using Microsoft.ML.Trainers.SymSgd;
 using Microsoft.ML.Transforms.Projections;
 
 namespace Microsoft.ML
diff --git a/src/Microsoft.ML.HalLearners/OlsLinearRegression.cs b/src/Microsoft.ML.HalLearners/OlsLinearRegression.cs
index 18a5d97b97..31c01b6324 100644
--- a/src/Microsoft.ML.HalLearners/OlsLinearRegression.cs
+++ b/src/Microsoft.ML.HalLearners/OlsLinearRegression.cs
@@ -15,7 +15,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.HalLearners;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.HalLearners/SymSgdClassificationTrainer.cs b/src/Microsoft.ML.HalLearners/SymSgdClassificationTrainer.cs
index 8b7377bbe1..e23619e81c 100644
--- a/src/Microsoft.ML.HalLearners/SymSgdClassificationTrainer.cs
+++ b/src/Microsoft.ML.HalLearners/SymSgdClassificationTrainer.cs
@@ -16,8 +16,7 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
-using Microsoft.ML.Trainers.SymSgd;
+using Microsoft.ML.Trainers.HalLearners;
 using Microsoft.ML.Training;
 using Microsoft.ML.Transforms;
 
@@ -29,7 +28,7 @@
 
 [assembly: LoadableClass(typeof(void), typeof(SymSgdClassificationTrainer), null, typeof(SignatureEntryPointModule), SymSgdClassificationTrainer.LoadNameValue)]
 
-namespace Microsoft.ML.Trainers.SymSgd
+namespace Microsoft.ML.Trainers.HalLearners
 {
     using TPredictor = CalibratedModelParametersBase<LinearBinaryModelParameters,PlattCalibrator>;
 
diff --git a/src/Microsoft.ML.LightGBM/LightGbmBinaryTrainer.cs b/src/Microsoft.ML.LightGBM/LightGbmBinaryTrainer.cs
index e73bf4acb9..bf01aecbfd 100644
--- a/src/Microsoft.ML.LightGBM/LightGbmBinaryTrainer.cs
+++ b/src/Microsoft.ML.LightGBM/LightGbmBinaryTrainer.cs
@@ -13,7 +13,6 @@
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(LightGbmBinaryTrainer.Summary, typeof(LightGbmBinaryTrainer), typeof(Options),
diff --git a/src/Microsoft.ML.LightGBM/LightGbmMulticlassTrainer.cs b/src/Microsoft.ML.LightGBM/LightGbmMulticlassTrainer.cs
index a3ec3ffef5..3b2a1b56af 100644
--- a/src/Microsoft.ML.LightGBM/LightGbmMulticlassTrainer.cs
+++ b/src/Microsoft.ML.LightGBM/LightGbmMulticlassTrainer.cs
@@ -12,7 +12,7 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Trainers;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(LightGbmMulticlassTrainer.Summary, typeof(LightGbmMulticlassTrainer), typeof(Options),
diff --git a/src/Microsoft.ML.LightGBM/LightGbmRankingTrainer.cs b/src/Microsoft.ML.LightGBM/LightGbmRankingTrainer.cs
index 04e78e7a4d..559ed6b5e1 100644
--- a/src/Microsoft.ML.LightGBM/LightGbmRankingTrainer.cs
+++ b/src/Microsoft.ML.LightGBM/LightGbmRankingTrainer.cs
@@ -11,7 +11,6 @@
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(LightGbmRankingTrainer.UserName, typeof(LightGbmRankingTrainer), typeof(Options),
diff --git a/src/Microsoft.ML.LightGBM/LightGbmRegressionTrainer.cs b/src/Microsoft.ML.LightGBM/LightGbmRegressionTrainer.cs
index 676627aa84..2068746cea 100644
--- a/src/Microsoft.ML.LightGBM/LightGbmRegressionTrainer.cs
+++ b/src/Microsoft.ML.LightGBM/LightGbmRegressionTrainer.cs
@@ -10,7 +10,6 @@
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(LightGbmRegressorTrainer.Summary, typeof(LightGbmRegressorTrainer), typeof(Options),
diff --git a/src/Microsoft.ML.LightGBM/LightGbmTrainerBase.cs b/src/Microsoft.ML.LightGBM/LightGbmTrainerBase.cs
index 1ce9b0ccf4..695157f91b 100644
--- a/src/Microsoft.ML.LightGBM/LightGbmTrainerBase.cs
+++ b/src/Microsoft.ML.LightGBM/LightGbmTrainerBase.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Training;
 
 namespace Microsoft.ML.LightGBM
diff --git a/src/Microsoft.ML.LightGBM/WrappedLightGbmBooster.cs b/src/Microsoft.ML.LightGBM/WrappedLightGbmBooster.cs
index 04dc9f9f62..cf8b9e4c8e 100644
--- a/src/Microsoft.ML.LightGBM/WrappedLightGbmBooster.cs
+++ b/src/Microsoft.ML.LightGBM/WrappedLightGbmBooster.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 
 namespace Microsoft.ML.LightGBM
 {
diff --git a/src/Microsoft.ML.ResultProcessor/ResultProcessor.cs b/src/Microsoft.ML.ResultProcessor/ResultProcessor.cs
index 2ce8ae224f..ba7f0b2fce 100644
--- a/src/Microsoft.ML.ResultProcessor/ResultProcessor.cs
+++ b/src/Microsoft.ML.ResultProcessor/ResultProcessor.cs
@@ -12,6 +12,7 @@
 using Microsoft.ML.Command;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
+using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Tools;
@@ -20,7 +21,7 @@
 using Microsoft.ML.ExperimentVisualization;
 #endif
 
-namespace Microsoft.ML.Internal.Internallearn.ResultProcessor
+namespace Microsoft.ML.ResultProcessor
 {
     using Float = System.Single;
     /// <summary>
diff --git a/src/Microsoft.ML.StandardLearners/Standard/LinearModelParameters.cs b/src/Microsoft.ML.StandardLearners/Standard/LinearModelParameters.cs
index 3a88c62128..df52e48433 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/LinearModelParameters.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/LinearModelParameters.cs
@@ -14,11 +14,11 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.Model.Pfa;
 using Microsoft.ML.Numeric;
+using Microsoft.ML.Trainers;
 using Newtonsoft.Json.Linq;
 
 // This is for deserialization from a model repository.
@@ -36,7 +36,7 @@
     "Poisson Regression Executor",
     PoissonRegressionModelParameters.LoaderSignature)]
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     public abstract class LinearModelParameters : ModelParametersBase<float>,
         IValueMapper,
diff --git a/src/Microsoft.ML.StandardLearners/Standard/LinearPredictorUtils.cs b/src/Microsoft.ML.StandardLearners/Standard/LinearPredictorUtils.cs
index f9f64f3689..b61f443e02 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/LinearPredictorUtils.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/LinearPredictorUtils.cs
@@ -13,7 +13,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Float = System.Single;
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     /// <summary>
     /// Helper methods for linear predictors
diff --git a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LbfgsPredictorBase.cs b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LbfgsPredictorBase.cs
index c6c6602c52..ca335e033f 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LbfgsPredictorBase.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LbfgsPredictorBase.cs
@@ -14,7 +14,7 @@
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Training;
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     public abstract class LbfgsTrainerBase<TArgs, TTransformer, TModel> : TrainerEstimatorBase<TTransformer, TModel>
       where TTransformer : ISingleFeaturePredictionTransformer<TModel>
diff --git a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LogisticRegression.cs b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LogisticRegression.cs
index 3cdb24c7df..d5fe66bdd7 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LogisticRegression.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/LogisticRegression.cs
@@ -13,8 +13,8 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(LogisticRegression.Summary, typeof(LogisticRegression), typeof(LogisticRegression.Options),
@@ -26,7 +26,7 @@
 
 [assembly: LoadableClass(typeof(void), typeof(LogisticRegression), null, typeof(SignatureEntryPointModule), LogisticRegression.LoadNameValue)]
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
 
     /// <include file='doc.xml' path='doc/members/member[@name="LBFGS"]/*' />
diff --git a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs
index 0ed3f008da..9d809dd3fa 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/LogisticRegression/MulticlassLogisticRegression.cs
@@ -14,7 +14,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.Model.Pfa;
@@ -35,7 +34,7 @@
     "Multiclass LR Executor",
     MulticlassLogisticRegressionModelParameters.LoaderSignature)]
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     /// <include file = 'doc.xml' path='doc/members/member[@name="LBFGS"]/*' />
     /// <include file = 'doc.xml' path='docs/members/example[@name="LogisticRegressionClassifier"]/*' />
diff --git a/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs b/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
index b5cb2813af..e5c2854da7 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/ModelStatistics.cs
@@ -12,15 +12,15 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers;
 
 // This is for deserialization from a model repository.
 [assembly: LoadableClass(typeof(LinearModelStatistics), null, typeof(SignatureLoadModel),
     "Linear Model Statistics",
     LinearModelStatistics.LoaderSignature)]
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     /// <summary>
     /// Represents a coefficient statistics object.
diff --git a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/MetaMulticlassTrainer.cs b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/MetaMulticlassTrainer.cs
index fdd32f2f20..448a078a8b 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/MetaMulticlassTrainer.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/MetaMulticlassTrainer.cs
@@ -14,7 +14,7 @@
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     using TScalarTrainer = ITrainerEstimator<ISingleFeaturePredictionTransformer<IPredictorProducing<float>>, IPredictorProducing<float>>;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Ova.cs b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Ova.cs
index cf7a083985..53f0f35fca 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Ova.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Ova.cs
@@ -16,7 +16,6 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
 using Microsoft.ML.Model.Pfa;
 using Microsoft.ML.Trainers;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Pkpd.cs b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Pkpd.cs
index 5e3dd33f8b..344f9e5f6a 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Pkpd.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/MultiClass/Pkpd.cs
@@ -11,7 +11,6 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
index bf7a88d27d..85da68d1f9 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
@@ -9,7 +9,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 
 // TODO: Check if it works properly if Averaged is set to false
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
index b3659974fa..c3610c7dd1 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
@@ -11,7 +11,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
index 184a6554aa..e1382b3038 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
@@ -12,7 +12,6 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
index 39ed31a6ec..8982127170 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
@@ -10,7 +10,6 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
index 662afe9a01..b41890a5fa 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
@@ -11,7 +11,6 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Training;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/PoissonRegression/PoissonRegression.cs b/src/Microsoft.ML.StandardLearners/Standard/PoissonRegression/PoissonRegression.cs
index caf4a37166..b389fc6034 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/PoissonRegression/PoissonRegression.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/PoissonRegression/PoissonRegression.cs
@@ -10,7 +10,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/SdcaBinary.cs b/src/Microsoft.ML.StandardLearners/Standard/SdcaBinary.cs
index 191c906b78..ca07f7fbe5 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/SdcaBinary.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/SdcaBinary.cs
@@ -18,7 +18,6 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/SdcaMultiClass.cs b/src/Microsoft.ML.StandardLearners/Standard/SdcaMultiClass.cs
index aceabb7b82..ff49788028 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/SdcaMultiClass.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/SdcaMultiClass.cs
@@ -14,7 +14,6 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Numeric;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
diff --git a/src/Microsoft.ML.StandardLearners/Standard/SdcaRegression.cs b/src/Microsoft.ML.StandardLearners/Standard/SdcaRegression.cs
index 49b65abe89..9524c48365 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/SdcaRegression.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/SdcaRegression.cs
@@ -12,7 +12,6 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/StochasticTrainerBase.cs b/src/Microsoft.ML.StandardLearners/Standard/StochasticTrainerBase.cs
index 10fa3b75c8..d9265513f8 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/StochasticTrainerBase.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/StochasticTrainerBase.cs
@@ -10,7 +10,7 @@
 using Microsoft.ML.Training;
 using Microsoft.ML.Transforms;
 
-namespace Microsoft.ML.Learners
+namespace Microsoft.ML.Trainers
 {
     public abstract class StochasticTrainerBase<TTransformer, TModel> : TrainerEstimatorBase<TTransformer, TModel>
         where TTransformer : ISingleFeaturePredictionTransformer<TModel>
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 6442e4eb01..ae03b46bc8 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -5,7 +5,6 @@
 using System;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
@@ -293,7 +292,7 @@ public static OnlineGradientDescentTrainer OnlineGradientDescent(this Regression
         }
 
         /// <summary>
-        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        ///  Predict a target using a linear binary classification model trained with the <see cref="Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The binary classificaiton catalog trainer object.</param>
         /// <param name="labelColumn">The label column name, or dependent variable.</param>
@@ -302,7 +301,7 @@ public static OnlineGradientDescentTrainer OnlineGradientDescent(this Regression
         /// <param name="enforceNoNegativity">Enforce non-negative weights.</param>
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Trainers.LogisticRegression"/>. Low=faster, less accurate.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
         /// <example>
         /// <format type="text/markdown">
@@ -327,7 +326,7 @@ public static LogisticRegression LogisticRegression(this BinaryClassificationCat
         }
 
         /// <summary>
-        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        ///  Predict a target using a linear binary classification model trained with the <see cref="Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The binary classificaiton catalog trainer object.</param>
         /// <param name="options">Advanced arguments to the algorithm.</param>
@@ -341,7 +340,7 @@ public static LogisticRegression LogisticRegression(this BinaryClassificationCat
         }
 
         /// <summary>
-        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Trainers.PoissonRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The regression catalog trainer object.</param>
         /// <param name="labelColumn">The labelColumn, or dependent variable.</param>
@@ -350,7 +349,7 @@ public static LogisticRegression LogisticRegression(this BinaryClassificationCat
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Trainers.PoissonRegression"/>. Low=faster, less accurate.</param>
         /// <param name="enforceNoNegativity">Enforce non-negative weights.</param>
         public static PoissonRegression PoissonRegression(this RegressionCatalog.RegressionTrainers catalog,
             string labelColumn = DefaultColumnNames.Label,
@@ -368,7 +367,7 @@ public static PoissonRegression PoissonRegression(this RegressionCatalog.Regress
         }
 
         /// <summary>
-        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Trainers.PoissonRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The regression catalog trainer object.</param>
         /// <param name="options">Advanced arguments to the algorithm.</param>
@@ -382,7 +381,7 @@ public static PoissonRegression PoissonRegression(this RegressionCatalog.Regress
         }
 
         /// <summary>
-        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Learners.MulticlassLogisticRegression"/> trainer.
+        /// Predict a target using a linear multiclass classification model trained with the <see cref="MulticlassLogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The <see cref="MulticlassClassificationCatalog.MulticlassClassificationTrainers"/>.</param>
         /// <param name="labelColumn">The labelColumn, or dependent variable.</param>
@@ -391,7 +390,7 @@ public static PoissonRegression PoissonRegression(this RegressionCatalog.Regress
         /// <param name="enforceNoNegativity">Enforce non-negative weights.</param>
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Trainers.MulticlassLogisticRegression"/>. Low=faster, less accurate.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
         public static MulticlassLogisticRegression LogisticRegression(this MulticlassClassificationCatalog.MulticlassClassificationTrainers catalog,
             string labelColumn = DefaultColumnNames.Label,
@@ -409,7 +408,7 @@ public static MulticlassLogisticRegression LogisticRegression(this MulticlassCla
         }
 
         /// <summary>
-        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Learners.MulticlassLogisticRegression"/> trainer.
+        /// Predict a target using a linear multiclass classification model trained with the <see cref="MulticlassLogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The <see cref="MulticlassClassificationCatalog.MulticlassClassificationTrainers"/>.</param>
         /// <param name="options">Advanced arguments to the algorithm.</param>
diff --git a/src/Microsoft.ML.StaticPipe/LbfgsStatic.cs b/src/Microsoft.ML.StaticPipe/LbfgsStatic.cs
index 5de22b6e89..7e2ed821c7 100644
--- a/src/Microsoft.ML.StaticPipe/LbfgsStatic.cs
+++ b/src/Microsoft.ML.StaticPipe/LbfgsStatic.cs
@@ -6,7 +6,6 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.StaticPipe.Runtime;
 using Microsoft.ML.Trainers;
 
@@ -20,7 +19,7 @@ namespace Microsoft.ML.StaticPipe
     public static class LbfgsBinaryClassificationStaticExtensions
     {
         /// <summary>
-        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The binary classificaiton catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
@@ -29,7 +28,7 @@ public static class LbfgsBinaryClassificationStaticExtensions
         /// <param name="enoforceNoNegativity">Enforce non-negative weights.</param>
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Trainers.LogisticRegression"/>. Low=faster, less accurate.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
         /// <param name="onFit">A delegate that is called every time the
         /// <see cref="Estimator{TInShape, TOutShape, TTransformer}.Fit(DataView{TInShape})"/> method is called on the
@@ -66,7 +65,7 @@ public static (Scalar<float> score, Scalar<float> probability, Scalar<bool> pred
         }
 
         /// <summary>
-        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        ///  Predict a target using a linear binary classification model trained with the <see cref="Microsoft.ML.Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The binary classificaiton catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
@@ -116,7 +115,7 @@ public static (Scalar<float> score, Scalar<float> probability, Scalar<bool> pred
     public static class LbfgsRegressionExtensions
     {
         /// <summary>
-        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The regression catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
@@ -125,7 +124,7 @@ public static class LbfgsRegressionExtensions
         /// <param name="enoforceNoNegativity">Enforce non-negative weights.</param>
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Trainers.LogisticRegression"/>. Low=faster, less accurate.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
         /// <param name="onFit">A delegate that is called every time the
         /// <see cref="Estimator{TInShape, TOutShape, TTransformer}.Fit(DataView{TInShape})"/> method is called on the
@@ -162,7 +161,7 @@ public static Scalar<float> PoissonRegression(this RegressionCatalog.RegressionT
         }
 
         /// <summary>
-        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Learners.LogisticRegression"/> trainer.
+        /// Predict a target using a linear regression model trained with the <see cref="Microsoft.ML.Trainers.LogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The regression catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
@@ -212,7 +211,7 @@ public static Scalar<float> PoissonRegression(this RegressionCatalog.RegressionT
     public static class LbfgsMulticlassExtensions
     {
         /// <summary>
-        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Learners.MulticlassLogisticRegression"/> trainer.
+        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Trainers.MulticlassLogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The multiclass classification catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
@@ -221,7 +220,7 @@ public static class LbfgsMulticlassExtensions
         /// <param name="enoforceNoNegativity">Enforce non-negative weights.</param>
         /// <param name="l1Weight">Weight of L1 regularization term.</param>
         /// <param name="l2Weight">Weight of L2 regularization term.</param>
-        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Learners.LogisticRegression"/>. Low=faster, less accurate.</param>
+        /// <param name="memorySize">Memory size for <see cref="Microsoft.ML.Trainers.LogisticRegression"/>. Low=faster, less accurate.</param>
         /// <param name="optimizationTolerance">Threshold for optimizer convergence.</param>
         /// <param name="onFit">A delegate that is called every time the
         /// <see cref="Estimator{TInShape, TOutShape, TTransformer}.Fit(DataView{TInShape})"/> method is called on the
@@ -258,7 +257,7 @@ public static (Vector<float> score, Key<uint, TVal> predictedLabel)
         }
 
         /// <summary>
-        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Learners.MulticlassLogisticRegression"/> trainer.
+        /// Predict a target using a linear multiclass classification model trained with the <see cref="Microsoft.ML.Trainers.MulticlassLogisticRegression"/> trainer.
         /// </summary>
         /// <param name="catalog">The multiclass classification catalog trainer object.</param>
         /// <param name="label">The label, or dependent variable.</param>
diff --git a/src/Microsoft.ML.StaticPipe/OnlineLearnerStatic.cs b/src/Microsoft.ML.StaticPipe/OnlineLearnerStatic.cs
index fea4867428..38165451a6 100644
--- a/src/Microsoft.ML.StaticPipe/OnlineLearnerStatic.cs
+++ b/src/Microsoft.ML.StaticPipe/OnlineLearnerStatic.cs
@@ -3,8 +3,8 @@
 // See the LICENSE file in the project root for more information.
 
 using System;
-using Microsoft.ML.Learners;
 using Microsoft.ML.StaticPipe.Runtime;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.Online;
 
 namespace Microsoft.ML.StaticPipe
diff --git a/src/Microsoft.ML.StaticPipe/SdcaStaticExtensions.cs b/src/Microsoft.ML.StaticPipe/SdcaStaticExtensions.cs
index 444a9fd7bc..62bc7f5e48 100644
--- a/src/Microsoft.ML.StaticPipe/SdcaStaticExtensions.cs
+++ b/src/Microsoft.ML.StaticPipe/SdcaStaticExtensions.cs
@@ -4,7 +4,6 @@
 
 using System;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.StaticPipe.Runtime;
 using Microsoft.ML.Trainers;
 
diff --git a/src/Microsoft.ML.Sweeper/Algorithms/KdoSweeper.cs b/src/Microsoft.ML.Sweeper/Algorithms/KdoSweeper.cs
index 7e0ca2c035..469988ec7c 100644
--- a/src/Microsoft.ML.Sweeper/Algorithms/KdoSweeper.cs
+++ b/src/Microsoft.ML.Sweeper/Algorithms/KdoSweeper.cs
@@ -9,7 +9,7 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Sweeper.Algorithms;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 using Float = System.Single;
 
 [assembly: LoadableClass(typeof(KdoSweeper), typeof(KdoSweeper.Arguments), typeof(SignatureSweeper),
diff --git a/src/Microsoft.ML.Sweeper/Algorithms/SmacSweeper.cs b/src/Microsoft.ML.Sweeper/Algorithms/SmacSweeper.cs
index 6c1738b297..8a81844eb0 100644
--- a/src/Microsoft.ML.Sweeper/Algorithms/SmacSweeper.cs
+++ b/src/Microsoft.ML.Sweeper/Algorithms/SmacSweeper.cs
@@ -13,7 +13,6 @@
 using Microsoft.ML.Sweeper;
 using Microsoft.ML.Sweeper.Algorithms;
 using Microsoft.ML.Trainers.FastTree;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Float = System.Single;
 
 [assembly: LoadableClass(typeof(SmacSweeper), typeof(SmacSweeper.Arguments), typeof(SignatureSweeper),
diff --git a/src/Microsoft.ML.Sweeper/ConfigRunner.cs b/src/Microsoft.ML.Sweeper/ConfigRunner.cs
index 082a3fb4c9..cbe2a99900 100644
--- a/src/Microsoft.ML.Sweeper/ConfigRunner.cs
+++ b/src/Microsoft.ML.Sweeper/ConfigRunner.cs
@@ -12,7 +12,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Sweeper;
 
-using ResultProcessorInternal = Microsoft.ML.Internal.Internallearn.ResultProcessor;
+using ResultProcessorInternal = Microsoft.ML.ResultProcessor;
 
 [assembly: LoadableClass(typeof(LocalExeConfigRunner), typeof(LocalExeConfigRunner.Arguments), typeof(SignatureConfigRunner),
     "Local Sweep Config Runner", "Local")]
diff --git a/src/Microsoft.ML.Sweeper/SweepResultEvaluator.cs b/src/Microsoft.ML.Sweeper/SweepResultEvaluator.cs
index 8b03b89cb9..a55f8d4647 100644
--- a/src/Microsoft.ML.Sweeper/SweepResultEvaluator.cs
+++ b/src/Microsoft.ML.Sweeper/SweepResultEvaluator.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
 using Microsoft.ML.Sweeper;
-using ResultProcessor = Microsoft.ML.Internal.Internallearn.ResultProcessor;
+using ResultProcessor = Microsoft.ML.ResultProcessor;
 
 [assembly: LoadableClass(typeof(InternalSweepResultEvaluator), typeof(InternalSweepResultEvaluator.Arguments), typeof(SignatureSweepResultEvaluator),
     "TLC Sweep Result Evaluator", "TlcEvaluator", "Tlc")]
diff --git a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
index 356268709c..256d946834 100644
--- a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
+++ b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
@@ -4,19 +4,19 @@ Data.IDataViewArrayConverter	Create an array variable of IDataView	Microsoft.ML.
 Data.PredictorModelArrayConverter	Create an array variable of PredictorModel	Microsoft.ML.EntryPoints.MacroUtils	MakeArray	Microsoft.ML.EntryPoints.MacroUtils+ArrayIPredictorModelInput	Microsoft.ML.EntryPoints.MacroUtils+ArrayIPredictorModelOutput
 Data.TextLoader	Import a dataset from a text file	Microsoft.ML.EntryPoints.ImportTextData	TextLoader	Microsoft.ML.EntryPoints.ImportTextData+LoaderInput	Microsoft.ML.EntryPoints.ImportTextData+Output
 Models.AnomalyDetectionEvaluator	Evaluates an anomaly detection scored dataset.	Microsoft.ML.Data.Evaluate	AnomalyDetection	Microsoft.ML.Data.AnomalyDetectionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.AnomalyPipelineEnsemble	Combine anomaly detection models into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateAnomalyPipelineEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+PipelineAnomalyInput	Microsoft.ML.EntryPoints.CommonOutputs+AnomalyDetectionOutput
+Models.AnomalyPipelineEnsemble	Combine anomaly detection models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateAnomalyPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineAnomalyInput	Microsoft.ML.EntryPoints.CommonOutputs+AnomalyDetectionOutput
 Models.BinaryClassificationEvaluator	Evaluates a binary classification scored dataset.	Microsoft.ML.Data.Evaluate	Binary	Microsoft.ML.Data.BinaryClassifierMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+ClassificationEvaluateOutput
-Models.BinaryEnsemble	Combine binary classifiers into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateBinaryEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+ClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Models.BinaryPipelineEnsemble	Combine binary classification models into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateBinaryPipelineEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Models.BinaryEnsemble	Combine binary classifiers into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+ClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Models.BinaryPipelineEnsemble	Combine binary classification models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateBinaryPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Models.ClassificationEvaluator	Evaluates a multi class classification scored dataset.	Microsoft.ML.Data.Evaluate	MultiClass	Microsoft.ML.Data.MultiClassMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+ClassificationEvaluateOutput
 Models.ClusterEvaluator	Evaluates a clustering scored dataset.	Microsoft.ML.Data.Evaluate	Clustering	Microsoft.ML.Data.ClusteringMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.CrossValidationResultsCombiner	Combine the metric data views returned from cross validation.	Microsoft.ML.EntryPoints.CrossValidationMacro	CombineMetrics	Microsoft.ML.EntryPoints.CrossValidationMacro+CombineMetricsInput	Microsoft.ML.EntryPoints.CrossValidationMacro+CombinedOutput
 Models.CrossValidator	Cross validation for general learning	Microsoft.ML.EntryPoints.CrossValidationMacro	CrossValidate	Microsoft.ML.EntryPoints.CrossValidationMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.CrossValidationMacro+Output]
 Models.CrossValidatorDatasetSplitter	Split the dataset into the specified number of cross-validation folds (train and test sets)	Microsoft.ML.EntryPoints.CVSplit	Split	Microsoft.ML.EntryPoints.CVSplit+Input	Microsoft.ML.EntryPoints.CVSplit+Output
 Models.DatasetTransformer	Applies a TransformModel to a dataset.	Microsoft.ML.EntryPoints.ModelOperations	Apply	Microsoft.ML.EntryPoints.ModelOperations+ApplyTransformModelInput	Microsoft.ML.EntryPoints.ModelOperations+ApplyTransformModelOutput
-Models.EnsembleSummary	Summarize a pipeline ensemble predictor.	Microsoft.ML.Ensemble.EntryPoints.PipelineEnsemble	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.Ensemble.EntryPoints.PipelineEnsemble+SummaryOutput
+Models.EnsembleSummary	Summarize a pipeline ensemble predictor.	Microsoft.ML.Ensemble.PipelineEnsemble	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.Ensemble.PipelineEnsemble+SummaryOutput
 Models.FixedPlattCalibrator	Apply a Platt calibrator with a fixed slope and offset to an input model	Microsoft.ML.Internal.Calibration.Calibrate	FixedPlatt	Microsoft.ML.Internal.Calibration.Calibrate+FixedPlattInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
-Models.MultiClassPipelineEnsemble	Combine multiclass classifiers into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateMultiClassPipelineEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
+Models.MultiClassPipelineEnsemble	Combine multiclass classifiers into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateMultiClassPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
 Models.MultiOutputRegressionEvaluator	Evaluates a multi output regression scored dataset.	Microsoft.ML.Data.Evaluate	MultiOutputRegression	Microsoft.ML.Data.MultiOutputRegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.NaiveCalibrator	Apply a Naive calibrator to an input model	Microsoft.ML.Internal.Calibration.Calibrate	Naive	Microsoft.ML.Internal.Calibration.Calibrate+NoArgumentsInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
 Models.OneVersusAll	One-vs-All macro (OVA)	Microsoft.ML.EntryPoints.OneVersusAllMacro	OneVersusAll	Microsoft.ML.EntryPoints.OneVersusAllMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.OneVersusAllMacro+Output]
@@ -26,9 +26,9 @@ Models.PAVCalibrator	Apply a PAV calibrator to an input model	Microsoft.ML.Inter
 Models.PlattCalibrator	Apply a Platt calibrator to an input model	Microsoft.ML.Internal.Calibration.Calibrate	Platt	Microsoft.ML.Internal.Calibration.Calibrate+NoArgumentsInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
 Models.QuantileRegressionEvaluator	Evaluates a quantile regression scored dataset.	Microsoft.ML.Data.Evaluate	QuantileRegression	Microsoft.ML.Data.QuantileRegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.RankerEvaluator	Evaluates a ranking scored dataset.	Microsoft.ML.Data.Evaluate	Ranking	Microsoft.ML.Data.RankerMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.RegressionEnsemble	Combine regression models into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateRegressionEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+RegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Models.RegressionEnsemble	Combine regression models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateRegressionEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+RegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Models.RegressionEvaluator	Evaluates a regression scored dataset.	Microsoft.ML.Data.Evaluate	Regression	Microsoft.ML.Data.RegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.RegressionPipelineEnsemble	Combine regression models into an ensemble	Microsoft.ML.EntryPoints.EnsembleCreator	CreateRegressionPipelineEnsemble	Microsoft.ML.EntryPoints.EnsembleCreator+PipelineRegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Models.RegressionPipelineEnsemble	Combine regression models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateRegressionPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineRegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Models.Summarizer	Summarize a linear regression predictor.	Microsoft.ML.EntryPoints.SummarizePredictor	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.EntryPoints.CommonOutputs+SummaryOutput
 Models.TrainTestEvaluator	General train test for any supported evaluator	Microsoft.ML.EntryPoints.TrainTestMacro	TrainTest	Microsoft.ML.EntryPoints.TrainTestMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.TrainTestMacro+Output]
 TimeSeriesProcessingEntryPoints.ExponentialAverage	Applies a Exponential average on a time series.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	ExponentialAverage	Microsoft.ML.TimeSeriesProcessing.ExponentialAverageTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
@@ -40,9 +40,9 @@ TimeSeriesProcessingEntryPoints.SlidingWindowTransform	Returns the last values f
 TimeSeriesProcessingEntryPoints.SsaChangePointDetector	This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaChangePointDetector	Microsoft.ML.TimeSeriesProcessing.SsaChangePointDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 TimeSeriesProcessingEntryPoints.SsaSpikeDetector	This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaSpikeDetector	Microsoft.ML.TimeSeriesProcessing.SsaSpikeDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 Trainers.AveragedPerceptronBinaryClassifier	Averaged Perceptron Binary Classifier.	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer	TrainBinary	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.EnsembleBinaryClassifier	Train binary ensemble.	Microsoft.ML.Ensemble.EntryPoints.Ensemble	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.EnsembleClassification	Train multiclass ensemble.	Microsoft.ML.Ensemble.EntryPoints.Ensemble	CreateMultiClassEnsemble	Microsoft.ML.Ensemble.MulticlassDataPartitionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
-Trainers.EnsembleRegression	Train regression ensemble.	Microsoft.ML.Ensemble.EntryPoints.Ensemble	CreateRegressionEnsemble	Microsoft.ML.Ensemble.RegressionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Trainers.EnsembleBinaryClassifier	Train binary ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Trainers.EnsembleClassification	Train multiclass ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateMultiClassEnsemble	Microsoft.ML.Ensemble.MulticlassDataPartitionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
+Trainers.EnsembleRegression	Train regression ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateRegressionEnsemble	Microsoft.ML.Ensemble.RegressionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.FastForestBinaryClassifier	Uses a random forest learner to perform binary classification.	Microsoft.ML.Trainers.FastTree.FastForest	TrainBinary	Microsoft.ML.Trainers.FastTree.FastForestClassification+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Trainers.FastForestRegressor	Trains a random forest to fit target values using least-squares.	Microsoft.ML.Trainers.FastTree.FastForest	TrainRegression	Microsoft.ML.Trainers.FastTree.FastForestRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.FastTreeBinaryClassifier	Uses a logit-boost boosted tree learner to perform binary classification.	Microsoft.ML.Trainers.FastTree.FastTree	TrainBinary	Microsoft.ML.Trainers.FastTree.FastTreeBinaryClassificationTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
@@ -58,8 +58,8 @@ Trainers.LightGbmClassifier	Train a LightGBM multi class model.	Microsoft.ML.Lig
 Trainers.LightGbmRanker	Train a LightGBM ranking model.	Microsoft.ML.LightGBM.LightGbm	TrainRanking	Microsoft.ML.LightGBM.Options	Microsoft.ML.EntryPoints.CommonOutputs+RankingOutput
 Trainers.LightGbmRegressor	LightGBM Regression	Microsoft.ML.LightGBM.LightGbm	TrainRegression	Microsoft.ML.LightGBM.Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.LinearSvmBinaryClassifier	Train a linear SVM.	Microsoft.ML.Trainers.Online.LinearSvmTrainer	TrainLinearSvm	Microsoft.ML.Trainers.Online.LinearSvmTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.LogisticRegressionBinaryClassifier	Logistic Regression is a method in statistics used to predict the probability of occurrence of an event and can be used as a classification algorithm. The algorithm predicts the probability of occurrence of an event by fitting data to a logistical function.	Microsoft.ML.Learners.LogisticRegression	TrainBinary	Microsoft.ML.Learners.LogisticRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.LogisticRegressionClassifier	Logistic Regression is a method in statistics used to predict the probability of occurrence of an event and can be used as a classification algorithm. The algorithm predicts the probability of occurrence of an event by fitting data to a logistical function.	Microsoft.ML.Learners.LogisticRegression	TrainMultiClass	Microsoft.ML.Learners.MulticlassLogisticRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
+Trainers.LogisticRegressionBinaryClassifier	Logistic Regression is a method in statistics used to predict the probability of occurrence of an event and can be used as a classification algorithm. The algorithm predicts the probability of occurrence of an event by fitting data to a logistical function.	Microsoft.ML.Trainers.LogisticRegression	TrainBinary	Microsoft.ML.Trainers.LogisticRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Trainers.LogisticRegressionClassifier	Logistic Regression is a method in statistics used to predict the probability of occurrence of an event and can be used as a classification algorithm. The algorithm predicts the probability of occurrence of an event by fitting data to a logistical function.	Microsoft.ML.Trainers.LogisticRegression	TrainMultiClass	Microsoft.ML.Trainers.MulticlassLogisticRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
 Trainers.NaiveBayesClassifier	Train a MultiClassNaiveBayesTrainer.	Microsoft.ML.Trainers.MultiClassNaiveBayesTrainer	TrainMultiClassNaiveBayesTrainer	Microsoft.ML.Trainers.MultiClassNaiveBayesTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
 Trainers.OnlineGradientDescentRegressor	Train a Online gradient descent perceptron.	Microsoft.ML.Trainers.Online.OnlineGradientDescentTrainer	TrainRegression	Microsoft.ML.Trainers.Online.OnlineGradientDescentTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.OrdinaryLeastSquaresRegressor	Train an OLS regression model.	Microsoft.ML.Trainers.HalLearners.OlsLinearRegressionTrainer	TrainRegression	Microsoft.ML.Trainers.HalLearners.OlsLinearRegressionTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
@@ -69,7 +69,7 @@ Trainers.StochasticDualCoordinateAscentBinaryClassifier	Train an SDCA binary mod
 Trainers.StochasticDualCoordinateAscentClassifier	The SDCA linear multi-class classification trainer.	Microsoft.ML.Trainers.Sdca	TrainMultiClass	Microsoft.ML.Trainers.SdcaMultiClassTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
 Trainers.StochasticDualCoordinateAscentRegressor	The SDCA linear regression trainer.	Microsoft.ML.Trainers.Sdca	TrainRegression	Microsoft.ML.Trainers.SdcaRegressionTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.StochasticGradientDescentBinaryClassifier	Train an Hogwild SGD binary model.	Microsoft.ML.Trainers.StochasticGradientDescentClassificationTrainer	TrainBinary	Microsoft.ML.Trainers.StochasticGradientDescentClassificationTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.SymSgdBinaryClassifier	Train a symbolic SGD.	Microsoft.ML.Trainers.SymSgd.SymSgdClassificationTrainer	TrainSymSgd	Microsoft.ML.Trainers.SymSgd.SymSgdClassificationTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Trainers.SymSgdBinaryClassifier	Train a symbolic SGD.	Microsoft.ML.Trainers.HalLearners.SymSgdClassificationTrainer	TrainSymSgd	Microsoft.ML.Trainers.HalLearners.SymSgdClassificationTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Transforms.ApproximateBootstrapSampler	Approximate bootstrap sampling.	Microsoft.ML.Transforms.BootstrapSample	GetSample	Microsoft.ML.Transforms.BootstrapSamplingTransformer+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 Transforms.BinaryPredictionScoreColumnsRenamer	For binary prediction, it renames the PredictedLabel and Score columns to include the name of the positive class.	Microsoft.ML.EntryPoints.ScoreModel	RenameBinaryPredictionScoreColumns	Microsoft.ML.EntryPoints.ScoreModel+RenameBinaryPredictionScoreColumnsInput	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 Transforms.BinNormalizer	The values are assigned into equidensity bins and a value is mapped to its bin_number/number_of_bins.	Microsoft.ML.Data.Normalize	Bin	Microsoft.ML.Transforms.Normalizers.NormalizeTransform+BinArguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
diff --git a/test/Microsoft.ML.Benchmarks/KMeansAndLogisticRegressionBench.cs b/test/Microsoft.ML.Benchmarks/KMeansAndLogisticRegressionBench.cs
index 3c92045b31..870480ebbc 100644
--- a/test/Microsoft.ML.Benchmarks/KMeansAndLogisticRegressionBench.cs
+++ b/test/Microsoft.ML.Benchmarks/KMeansAndLogisticRegressionBench.cs
@@ -6,8 +6,8 @@
 using Microsoft.ML.Benchmarks.Harness;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.TestFramework;
+using Microsoft.ML.Trainers;
 
 namespace Microsoft.ML.Benchmarks
 {
diff --git a/test/Microsoft.ML.Benchmarks/StochasticDualCoordinateAscentClassifierBench.cs b/test/Microsoft.ML.Benchmarks/StochasticDualCoordinateAscentClassifierBench.cs
index 7c67d13ac4..c08118bee6 100644
--- a/test/Microsoft.ML.Benchmarks/StochasticDualCoordinateAscentClassifierBench.cs
+++ b/test/Microsoft.ML.Benchmarks/StochasticDualCoordinateAscentClassifierBench.cs
@@ -9,7 +9,6 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.Benchmarks.Harness;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
 using Microsoft.ML.TestFramework;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Transforms;
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
index 8fcb00842e..c9ae3f9da3 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
@@ -12,7 +12,7 @@
 using Microsoft.ML.Core.Tests.UnitTests;
 using Microsoft.ML.Data;
 using Microsoft.ML.Data.IO;
-using Microsoft.ML.Ensemble.EntryPoints;
+using Microsoft.ML.Ensemble;
 using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.EntryPoints.JsonUtils;
@@ -20,14 +20,13 @@
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.TimeSeriesProcessing;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
+using Microsoft.ML.Trainers.HalLearners;
 using Microsoft.ML.Trainers.PCA;
-using Microsoft.ML.Trainers.SymSgd;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.Categorical;
 using Microsoft.ML.Transforms.Conversions;
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestUtilities.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestUtilities.cs
index 9c0bd79e8e..e94de49320 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestUtilities.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestUtilities.cs
@@ -7,7 +7,6 @@
 using System.Linq;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.RunTests;
-using Microsoft.ML.Trainers.FastTree.Internal;
 using Xunit;
 using Xunit.Abstractions;
 
diff --git a/test/Microsoft.ML.Predictor.Tests/TestParallelFasttreeInterface.cs b/test/Microsoft.ML.Predictor.Tests/TestParallelFasttreeInterface.cs
index 6c246fa4a0..776431faba 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestParallelFasttreeInterface.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestParallelFasttreeInterface.cs
@@ -6,7 +6,7 @@
 using Microsoft.ML;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.RunTests;
-using Microsoft.ML.Trainers.FastTree.Internal;
+using Microsoft.ML.Trainers.FastTree;
 using Xunit;
 using Xunit.Abstractions;
 
diff --git a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
index 339bede3e5..4ecd744c36 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
@@ -17,14 +17,12 @@ namespace Microsoft.ML.RunTests
     using Microsoft.ML.Ensemble;
     using Microsoft.ML.EntryPoints;
     using Microsoft.ML.Internal.Utilities;
-    using Microsoft.ML.Learners;
     using Microsoft.ML.LightGBM;
     using Microsoft.ML.TestFramework;
+    using Microsoft.ML.Trainers;
     using Microsoft.ML.Trainers.FastTree;
-    using Microsoft.ML.Trainers.FastTree.Internal;
+    using Microsoft.ML.Trainers.HalLearners;
     using Microsoft.ML.Trainers.Online;
-    using Microsoft.ML.Trainers.SymSgd;
-    using Microsoft.ML.Transforms.Categorical;
     using Xunit;
     using Xunit.Abstractions;
     using TestLearners = TestLearnersBase;
diff --git a/test/Microsoft.ML.StaticPipelineTesting/Training.cs b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
index 1fdb3bae66..c14b5071d2 100644
--- a/test/Microsoft.ML.StaticPipelineTesting/Training.cs
+++ b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
@@ -10,11 +10,9 @@
 using Microsoft.ML.FactorizationMachine;
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Learners;
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.LightGBM.StaticPipe;
 using Microsoft.ML.RunTests;
-using Microsoft.ML.SamplesUtils;
 using Microsoft.ML.StaticPipe;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
@@ -1027,7 +1025,7 @@ public void MultiClassLightGbmStaticPipelineWithInMemoryData()
             var mlContext = new MLContext(seed: 1, conc: 1);
 
             // Create in-memory examples as C# native class.
-            var examples = DatasetUtils.GenerateRandomMulticlassClassificationExamples(1000);
+            var examples = SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(1000);
 
             // Convert native C# class to IDataView, a consumble format to ML.NET functions.
             var dataView = mlContext.Data.ReadFromEnumerable(examples);
@@ -1079,7 +1077,7 @@ public void MultiClassLightGbmStaticPipelineWithInMemoryData()
             Assert.Equal(0.86309523809523814, metrics.AccuracyMicro, 6);
 
             // Convert prediction in ML.NET format to native C# class.
-            var nativePredictions = mlContext.CreateEnumerable<DatasetUtils.MulticlassClassificationExample>(prediction.AsDynamic, false).ToList();
+            var nativePredictions = mlContext.CreateEnumerable<SamplesUtils.DatasetUtils.MulticlassClassificationExample>(prediction.AsDynamic, false).ToList();
 
             // Get schema object of the prediction. It contains metadata such as the mapping from predicted label index
             // (e.g., 1) to its actual label (e.g., "AA").
@@ -1087,7 +1085,7 @@ public void MultiClassLightGbmStaticPipelineWithInMemoryData()
 
             // Retrieve the mapping from labels to label indexes.
             var labelBuffer = new VBuffer<ReadOnlyMemory<char>>();
-            schema[nameof(DatasetUtils.MulticlassClassificationExample.PredictedLabelIndex)].Metadata.GetValue("KeyValues", ref labelBuffer);
+            schema[nameof(SamplesUtils.DatasetUtils.MulticlassClassificationExample.PredictedLabelIndex)].Metadata.GetValue("KeyValues", ref labelBuffer);
             var nativeLabels = labelBuffer.DenseValues().ToList(); // nativeLabels[nativePrediction.PredictedLabelIndex-1] is the original label indexed by nativePrediction.PredictedLabelIndex.
 
             // Show prediction result for the 3rd example.
diff --git a/test/Microsoft.ML.TestFramework/BaseTestPredictorsMaml.cs b/test/Microsoft.ML.TestFramework/BaseTestPredictorsMaml.cs
index 57b2983f44..16e2ef0325 100644
--- a/test/Microsoft.ML.TestFramework/BaseTestPredictorsMaml.cs
+++ b/test/Microsoft.ML.TestFramework/BaseTestPredictorsMaml.cs
@@ -9,7 +9,7 @@
 
 namespace Microsoft.ML.RunTests
 {
-    using ResultProcessor = Microsoft.ML.Internal.Internallearn.ResultProcessor.ResultProcessor;
+    using ResultProcessor = ResultProcessor.ResultProcessor;
 
     /// <summary>
     /// This is a base test class designed to support running trainings and related
diff --git a/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs b/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
index 1b8cbdcb60..36fccc7980 100644
--- a/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
+++ b/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
@@ -5,7 +5,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
-using Microsoft.ML.Learners;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Trainers.KMeans;
 using Microsoft.ML.Trainers.PCA;
diff --git a/test/Microsoft.ML.Tests/OnnxConversionTest.cs b/test/Microsoft.ML.Tests/OnnxConversionTest.cs
index f5b062c31a..00b7435d91 100644
--- a/test/Microsoft.ML.Tests/OnnxConversionTest.cs
+++ b/test/Microsoft.ML.Tests/OnnxConversionTest.cs
@@ -11,10 +11,10 @@
 using Google.Protobuf;
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.Tools;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.UniversalModelFormat.Onnx;
 using Newtonsoft.Json;
diff --git a/test/Microsoft.ML.Tests/PermutationFeatureImportanceTests.cs b/test/Microsoft.ML.Tests/PermutationFeatureImportanceTests.cs
index e6fa53c584..6be9b0adfe 100644
--- a/test/Microsoft.ML.Tests/PermutationFeatureImportanceTests.cs
+++ b/test/Microsoft.ML.Tests/PermutationFeatureImportanceTests.cs
@@ -8,8 +8,8 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
-using Microsoft.ML.Learners;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.Trainers;
 using Xunit;
 using Xunit.Abstractions;
 
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
index 01501677be..eab7d56fdb 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
@@ -10,7 +10,6 @@
 using Microsoft.ML;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
 using Microsoft.ML.TestFramework;
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
index 39957cc031..a0ef508417 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
@@ -11,9 +11,9 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.TestFramework;
+using Microsoft.ML.Trainers;
 using Microsoft.ML.Transforms.Categorical;
 using Microsoft.ML.Transforms.Normalizers;
 using Microsoft.ML.Transforms.Text;
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/IntrospectiveTraining.cs b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/IntrospectiveTraining.cs
index fd61f0eb77..5223cc0e38 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/IntrospectiveTraining.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/IntrospectiveTraining.cs
@@ -94,7 +94,7 @@ public void FastTreeClassificationIntrospectiveTraining()
         public void FastForestRegressionIntrospectiveTraining()
         {
             var ml = new MLContext(seed: 1, conc: 1);
-            var data = DatasetUtils.GenerateFloatLabelFloatFeatureVectorSamples(1000);
+            var data = SamplesUtils.DatasetUtils.GenerateFloatLabelFloatFeatureVectorSamples(1000);
             var dataView = ml.Data.ReadFromEnumerable(data);
 
             RegressionPredictionTransformer<FastForestRegressionModelParameters> pred = null;
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/SimpleTrainAndPredict.cs b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/SimpleTrainAndPredict.cs
index 6d70b43d61..7a328c73d6 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/SimpleTrainAndPredict.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/Estimators/SimpleTrainAndPredict.cs
@@ -6,7 +6,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.Trainers;
-using Microsoft.ML.Trainers.SymSgd;
+using Microsoft.ML.Trainers.HalLearners;
 using Xunit;
 
 namespace Microsoft.ML.Tests.Scenarios.Api
diff --git a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
index ecea5411a6..1ef0cda99c 100644
--- a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
@@ -3,8 +3,6 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
-using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Trainers.Online;
 using Xunit;
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/CalibratorEstimators.cs b/test/Microsoft.ML.Tests/TrainerEstimators/CalibratorEstimators.cs
index b3f4207de4..3de1a3125a 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/CalibratorEstimators.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/CalibratorEstimators.cs
@@ -6,8 +6,7 @@
 using Microsoft.ML.Calibrator;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.Learners;
-using Microsoft.ML.Trainers.Online;
+using Microsoft.ML.Trainers;
 using Xunit;
 
 namespace Microsoft.ML.Tests.TrainerEstimators
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/LbfgsTests.cs b/test/Microsoft.ML.Tests/TrainerEstimators/LbfgsTests.cs
index 134b52f96e..4e61c3480b 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/LbfgsTests.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/LbfgsTests.cs
@@ -2,12 +2,10 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Linq;
 using Microsoft.Data.DataView;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Trainers;
 using Xunit;
 
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/SymSgdClassificationTests.cs b/test/Microsoft.ML.Tests/TrainerEstimators/SymSgdClassificationTests.cs
index 5b1a698e95..1b4dbf86e2 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/SymSgdClassificationTests.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/SymSgdClassificationTests.cs
@@ -4,10 +4,9 @@
 
 using System.Linq;
 using Microsoft.ML.Data;
+using Microsoft.ML.Trainers.HalLearners;
 using Microsoft.ML.Internal.Calibration;
-using Microsoft.ML.Learners;
 using Microsoft.ML.Trainers;
-using Microsoft.ML.Trainers.SymSgd;
 using Xunit;
 
 namespace Microsoft.ML.Tests.TrainerEstimators
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
index 2cde2c918e..e6ced83d4d 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
@@ -2,12 +2,11 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System;
+using System.Linq;
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
-using Microsoft.ML.SamplesUtils;
 using Microsoft.ML.Trainers.FastTree;
-using System;
-using System.Linq;
 using Xunit;
 
 namespace Microsoft.ML.Tests.TrainerEstimators
@@ -18,7 +17,7 @@ public partial class TrainerEstimators
         public void TreeEnsembleFeaturizerOutputSchemaTest()
         {
             // Create data set
-            var data = DatasetUtils.GenerateBinaryLabelFloatFeatureVectorSamples(1000).ToList();
+            var data = SamplesUtils.DatasetUtils.GenerateBinaryLabelFloatFeatureVectorSamples(1000).ToList();
             var dataView = ML.Data.ReadFromEnumerable(data);
 
             // Define a tree model whose trees will be extracted to construct a tree featurizer.
@@ -38,8 +37,8 @@ public void TreeEnsembleFeaturizerOutputSchemaTest()
 
             // To get output schema, we need to create RoleMappedSchema for calling Bind(...).
             var roleMappedSchema = new RoleMappedSchema(dataView.Schema,
-                label: nameof(DatasetUtils.BinaryLabelFloatFeatureVectorSample.Label),
-                feature: nameof(DatasetUtils.BinaryLabelFloatFeatureVectorSample.Features));
+                label: nameof(SamplesUtils.DatasetUtils.BinaryLabelFloatFeatureVectorSample.Label),
+                feature: nameof(SamplesUtils.DatasetUtils.BinaryLabelFloatFeatureVectorSample.Features));
 
             // Retrieve output schema. 
             var boundMapper = (treeFeaturizer as ISchemaBindableMapper).Bind(Env, roleMappedSchema);
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
index ddaf368dd8..9f3dfe20ee 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
@@ -331,7 +331,7 @@ private void LightGbmHelper(bool useSoftmax, out string modelString, out List<Gb
                 floatLabels[i] = labels[i];
 
             // Allocate LightGBM data container (called Dataset in LightGBM world).
-            var gbmDataSet = new Dataset(sampleValueGroupedByColumn, sampleIndicesGroupedByColumn, _columnNumber, sampleNonZeroCntPerColumn, _rowNumber, _rowNumber, "", floatLabels);
+            var gbmDataSet = new LightGBM.Dataset(sampleValueGroupedByColumn, sampleIndicesGroupedByColumn, _columnNumber, sampleNonZeroCntPerColumn, _rowNumber, _rowNumber, "", floatLabels);
 
             // Push training examples into LightGBM data container.
             gbmDataSet.PushRows(dataMatrix, _rowNumber, _columnNumber, 0);

From 069abb757dec7309e4e3c23b40829f1cb73b39c6 Mon Sep 17 00:00:00 2001
From: Zeeshan Siddiqui <mzs@microsoft.com>
Date: Mon, 11 Feb 2019 12:16:50 -0500
Subject: [PATCH 10/23] Lockdown Microsoft.ML.TimeSeries public surface (#2344)

* Reduce public API by making non-estimator components internal.

* Internalize forecasting code.

* Make misc stuff internal.

* build break.

* Make time series state internal.

* Hide SequenceModelerBase

* PR feedback.

* PR feedback and hide more stuff.

* fix entrypoint catalog test.

* PR feedback.

* merge conflict resolved.
---
 .../IidChangePointDetectorTransform.cs        |   18 +-
 .../Dynamic/IidSpikeDetectorTransform.cs      |   19 +-
 .../SsaChangePointDetectorTransform.cs        |   24 +-
 .../Dynamic/SsaSpikeDetectorTransform.cs      |   22 +-
 .../Prediction/PredictionEngine.cs            |    2 +-
 .../TimeSeriesStatic.cs                       |   66 +-
 ...AdaptiveSingularSpectrumSequenceModeler.cs |    8 +-
 src/Microsoft.ML.TimeSeries/EigenUtils.cs     |    2 +-
 .../ExponentialAverageTransform.cs            |    4 +-
 .../ExtensionsCatalog.cs                      |   87 ++
 .../IidAnomalyDetectionBase.cs                |  207 ++-
 .../IidChangePointDetector.cs                 |  105 +-
 .../IidSpikeDetector.cs                       |   88 +-
 .../MovingAverageTransform.cs                 |    2 +-
 .../PValueTransform.cs                        |    2 +-
 .../PercentileThresholdTransform.cs           |    2 +-
 .../PolynomialUtils.cs                        |    2 +-
 .../PredictionFunction.cs                     |   15 +-
 .../Properties/AssemblyInfo.cs                |    3 +
 .../SequenceModelerBase.cs                    |    4 +-
 ...SequentialAnomalyDetectionTransformBase.cs | 1108 ++++++++---------
 .../SequentialTransformBase.cs                |    2 +-
 .../SequentialTransformerBase.cs              |   49 +-
 .../SlidingWindowTransform.cs                 |    2 +-
 .../SlidingWindowTransformBase.cs             |    2 +-
 .../SsaAnomalyDetectionBase.cs                |  387 +++---
 .../SsaChangePointDetector.cs                 |  128 +-
 .../SsaSpikeDetector.cs                       |  114 +-
 .../TimeSeriesProcessing.cs                   |   40 +-
 .../TrajectoryMatrix.cs                       |    2 +-
 .../Common/EntryPoints/core_ep-list.tsv       |    8 +-
 .../TimeSeriesDirectApi.cs                    |    8 +-
 32 files changed, 1385 insertions(+), 1147 deletions(-)
 create mode 100644 src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
index 7f588568c4..d7dbc8553b 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
@@ -54,16 +54,9 @@ public static void IidChangePointDetectorTransform()
             // Setup IidSpikeDetector arguments
             string outputColumnName = nameof(ChangePointPrediction.Prediction);
             string inputColumnName = nameof(IidChangePointData.Value);
-            var args = new IidChangePointDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                // The confidence for spike detection in the range [0, 100]
-                ChangeHistoryLength = Size / 4, // The length of the sliding window on p-values for computing the martingale score. 
-            };
 
             // The transformed data.
-            var transformedData = new IidChangePointEstimator(ml, args).Fit(dataView).Transform(dataView);
+            var transformedData = ml.Transforms.IidChangePointEstimator(outputColumnName, inputColumnName, 95, Size / 4).Fit(dataView).Transform(dataView);
 
             // Getting the data of the newly created column as an IEnumerable of ChangePointPrediction.
             var predictionColumn = ml.CreateEnumerable<ChangePointPrediction>(transformedData, reuseRowObject: false);
@@ -119,16 +112,9 @@ public static void IidChangePointDetectorPrediction()
             // Setup IidSpikeDetector arguments
             string outputColumnName = nameof(ChangePointPrediction.Prediction);
             string inputColumnName = nameof(IidChangePointData.Value);
-            var args = new IidChangePointDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                // The confidence for spike detection in the range [0, 100]
-                ChangeHistoryLength = Size / 4, // The length of the sliding window on p-values for computing the martingale score. 
-            };
 
             // Time Series model.
-            ITransformer model = new IidChangePointEstimator(ml, args).Fit(dataView);
+            ITransformer model = ml.Transforms.IidChangePointEstimator(outputColumnName, inputColumnName, 95, Size / 4).Fit(dataView);
 
             // Create a time series prediction engine from the model.
             var engine = model.CreateTimeSeriesPredictionFunction<IidChangePointData, ChangePointPrediction>(ml);
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
index bcbeaa36ee..c03449be17 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
@@ -51,16 +51,9 @@ public static void IidSpikeDetectorTransform()
             // Setup IidSpikeDetector arguments
             string outputColumnName = nameof(IidSpikePrediction.Prediction);
             string inputColumnName = nameof(IidSpikeData.Value);
-            var args = new IidSpikeDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                // The confidence for spike detection in the range [0, 100]
-                PvalueHistoryLength = Size / 4  // The size of the sliding window for computing the p-value; shorter windows are more sensitive to spikes.
-            };
 
             // The transformed data.
-            var transformedData = new IidSpikeEstimator(ml, args).Fit(dataView).Transform(dataView);
+            var transformedData = ml.Transforms.IidSpikeEstimator(outputColumnName, inputColumnName, 95, Size / 4).Fit(dataView).Transform(dataView);
 
             // Getting the data of the newly created column as an IEnumerable of IidSpikePrediction.
             var predictionColumn = ml.CreateEnumerable<IidSpikePrediction>(transformedData, reuseRowObject: false);
@@ -108,16 +101,8 @@ public static void IidSpikeDetectorPrediction()
             // Setup IidSpikeDetector arguments
             string outputColumnName = nameof(IidSpikePrediction.Prediction);
             string inputColumnName = nameof(IidSpikeData.Value);
-            var args = new IidSpikeDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                // The confidence for spike detection in the range [0, 100]
-                PvalueHistoryLength = Size / 4  // The size of the sliding window for computing the p-value; shorter windows are more sensitive to spikes.
-            };
-
             // The transformed model.
-            ITransformer model = new IidSpikeEstimator(ml, args).Fit(dataView);
+            ITransformer model = ml.Transforms.IidChangePointEstimator(outputColumnName, inputColumnName, 95, Size).Fit(dataView);
 
             // Create a time series prediction engine from the model.
             var engine = model.CreateTimeSeriesPredictionFunction<IidSpikeData, IidSpikePrediction>(ml);
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
index efc52a90dc..19bb5e75c5 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
@@ -49,19 +49,9 @@ public static void SsaChangePointDetectorTransform()
             // Setup SsaChangePointDetector arguments
             var inputColumnName = nameof(SsaChangePointData.Value);
             var outputColumnName = nameof(ChangePointPrediction.Prediction);
-            var args = new SsaChangePointDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                          // The confidence for spike detection in the range [0, 100]
-                ChangeHistoryLength = 8,                  // The length of the window for detecting a change in trend; shorter windows are more sensitive to spikes.
-                TrainingWindowSize = TrainingSize,        // The number of points from the beginning of the sequence used for training.
-                SeasonalWindowSize = SeasonalitySize + 1  // An upper bound on the largest relevant seasonality in the input time series."
-
-            };
 
             // The transformed data.
-            var transformedData = new SsaChangePointEstimator(ml, args).Fit(dataView).Transform(dataView);
+            var transformedData = ml.Transforms.SsaChangePointEstimator(outputColumnName, inputColumnName, 95, 8, TrainingSize, SeasonalitySize + 1).Fit(dataView).Transform(dataView);
 
             // Getting the data of the newly created column as an IEnumerable of ChangePointPrediction.
             var predictionColumn = ml.CreateEnumerable<ChangePointPrediction>(transformedData, reuseRowObject: false);
@@ -120,19 +110,9 @@ public static void SsaChangePointDetectorPrediction()
             // Setup SsaChangePointDetector arguments
             var inputColumnName = nameof(SsaChangePointData.Value);
             var outputColumnName = nameof(ChangePointPrediction.Prediction);
-            var args = new SsaChangePointDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                         // The confidence for spike detection in the range [0, 100]
-                ChangeHistoryLength = 8,                 // The length of the window for detecting a change in trend; shorter windows are more sensitive to spikes. 
-                TrainingWindowSize = TrainingSize,       // The number of points from the beginning of the sequence used for training.
-                SeasonalWindowSize = SeasonalitySize + 1 // An upper bound on the largest relevant seasonality in the input time series."
-
-            };
 
             // Train the change point detector.
-            ITransformer model = new SsaChangePointEstimator(ml, args).Fit(dataView);
+            ITransformer model = ml.Transforms.SsaChangePointEstimator(outputColumnName, inputColumnName, 95, 8, TrainingSize, SeasonalitySize + 1).Fit(dataView);
 
             // Create a prediction engine from the model for feeding new data.
             var engine = model.CreateTimeSeriesPredictionFunction<SsaChangePointData, ChangePointPrediction>(ml);
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
index db8bfcad70..217ddb69ed 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
@@ -56,18 +56,9 @@ public static void SsaSpikeDetectorTransform()
             // Setup IidSpikeDetector arguments
             var inputColumnName = nameof(SsaSpikeData.Value);
             var outputColumnName = nameof(SsaSpikePrediction.Prediction);
-            var args = new SsaSpikeDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                          // The confidence for spike detection in the range [0, 100]
-                PvalueHistoryLength = 8,                  // The size of the sliding window for computing the p-value; shorter windows are more sensitive to spikes.
-                TrainingWindowSize = TrainingSize,        // The number of points from the beginning of the sequence used for training.
-                SeasonalWindowSize = SeasonalitySize + 1  // An upper bound on the largest relevant seasonality in the input time series."
-            };
 
             // The transformed data.
-            var transformedData = new SsaSpikeEstimator(ml, args).Fit(dataView).Transform(dataView);
+            var transformedData = ml.Transforms.SsaSpikeEstimator(outputColumnName, inputColumnName, 95, 8, TrainingSize, SeasonalitySize + 1).Fit(dataView).Transform(dataView);
 
             // Getting the data of the newly created column as an IEnumerable of SsaSpikePrediction.
             var predictionColumn = ml.CreateEnumerable<SsaSpikePrediction>(transformedData, reuseRowObject: false);
@@ -127,18 +118,9 @@ public static void SsaSpikeDetectorPrediction()
             // Setup IidSpikeDetector arguments
             var inputColumnName = nameof(SsaSpikeData.Value);
             var outputColumnName = nameof(SsaSpikePrediction.Prediction);
-            var args = new SsaSpikeDetector.Arguments()
-            {
-                Source = inputColumnName,
-                Name = outputColumnName,
-                Confidence = 95,                          // The confidence for spike detection in the range [0, 100]
-                PvalueHistoryLength = 8,                  // The size of the sliding window for computing the p-value; shorter windows are more sensitive to spikes.
-                TrainingWindowSize = TrainingSize,        // The number of points from the beginning of the sequence used for training.
-                SeasonalWindowSize = SeasonalitySize + 1  // An upper bound on the largest relevant seasonality in the input time series."
-            };
 
             // Train the change point detector.
-            ITransformer model = new SsaSpikeEstimator(ml, args).Fit(dataView);
+            ITransformer model = ml.Transforms.SsaChangePointEstimator(outputColumnName, inputColumnName, 95, 8, TrainingSize, SeasonalitySize + 1).Fit(dataView);
 
             // Create a prediction engine from the model for feeding new data.
             var engine = model.CreateTimeSeriesPredictionFunction<SsaSpikeData, SsaSpikePrediction>(ml);
diff --git a/src/Microsoft.ML.Data/Prediction/PredictionEngine.cs b/src/Microsoft.ML.Data/Prediction/PredictionEngine.cs
index 55e30203d4..d90539cc80 100644
--- a/src/Microsoft.ML.Data/Prediction/PredictionEngine.cs
+++ b/src/Microsoft.ML.Data/Prediction/PredictionEngine.cs
@@ -146,7 +146,7 @@ private protected virtual void PredictionEngineCore(IHostEnvironment env, DataVi
             disposer = inputRow.Dispose;
         }
 
-        protected virtual Func<Schema, IRowToRowMapper> TransformerChecker(IExceptionContext ectx, ITransformer transformer)
+        private protected virtual Func<Schema, IRowToRowMapper> TransformerChecker(IExceptionContext ectx, ITransformer transformer)
         {
             ectx.CheckValue(transformer, nameof(transformer));
             ectx.CheckParam(transformer.IsRowToRowMapper, nameof(transformer), "Must be a row to row mapper");
diff --git a/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs b/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
index 8e8f7e137b..85398dded2 100644
--- a/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
+++ b/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
@@ -9,8 +9,6 @@
 
 namespace Microsoft.ML.StaticPipe
 {
-    using IidBase = Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<float, Microsoft.ML.TimeSeriesProcessing.IidAnomalyDetectionBase.State>;
-    using SsaBase = Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<float, Microsoft.ML.TimeSeriesProcessing.SsaAnomalyDetectionBase.State>;
 
     /// <summary>
     /// Static API extension methods for <see cref="IidChangePointEstimator"/>.
@@ -25,7 +23,7 @@ public OutColumn(
                 Scalar<float> input,
                 int confidence,
                 int changeHistoryLength,
-                IidBase.MartingaleType martingale,
+                MartingaleType martingale,
                 double eps)
                 : base(new Reconciler(confidence, changeHistoryLength, martingale, eps), input)
             {
@@ -37,13 +35,13 @@ private sealed class Reconciler : EstimatorReconciler
         {
             private readonly int _confidence;
             private readonly int _changeHistoryLength;
-            private readonly IidBase.MartingaleType _martingale;
+            private readonly MartingaleType _martingale;
             private readonly double _eps;
 
             public Reconciler(
                 int confidence,
                 int changeHistoryLength,
-                IidBase.MartingaleType martingale,
+                MartingaleType martingale,
                 double eps)
             {
                 _confidence = confidence;
@@ -60,11 +58,11 @@ public override IEstimator<ITransformer> Reconcile(IHostEnvironment env,
             {
                 Contracts.Assert(toOutput.Length == 1);
                 var outCol = (OutColumn)toOutput[0];
-                return new IidChangePointEstimator(env,
+                return new MLContext().Transforms.IidChangePointEstimator(
                     outputNames[outCol],
+                    inputNames[outCol.Input],
                     _confidence,
                     _changeHistoryLength,
-                    inputNames[outCol.Input],
                     _martingale,
                     _eps);
             }
@@ -77,7 +75,7 @@ public static Vector<double> IidChangePointDetect(
             this Scalar<float> input,
             int confidence,
             int changeHistoryLength,
-            IidBase.MartingaleType martingale = IidBase.MartingaleType.Power,
+            MartingaleType martingale = MartingaleType.Power,
             double eps = 0.1) => new OutColumn(input, confidence, changeHistoryLength, martingale, eps);
     }
 
@@ -93,7 +91,7 @@ private sealed class OutColumn : Vector<double>
             public OutColumn(Scalar<float> input,
                 int confidence,
                 int pvalueHistoryLength,
-                IidBase.AnomalySide side)
+                AnomalySide side)
                 : base(new Reconciler(confidence, pvalueHistoryLength, side), input)
             {
                 Input = input;
@@ -104,12 +102,12 @@ private sealed class Reconciler : EstimatorReconciler
         {
             private readonly int _confidence;
             private readonly int _pvalueHistoryLength;
-            private readonly IidBase.AnomalySide _side;
+            private readonly AnomalySide _side;
 
             public Reconciler(
                 int confidence,
                 int pvalueHistoryLength,
-                IidBase.AnomalySide side)
+                AnomalySide side)
             {
                 _confidence = confidence;
                 _pvalueHistoryLength = pvalueHistoryLength;
@@ -124,11 +122,11 @@ public override IEstimator<ITransformer> Reconcile(IHostEnvironment env,
             {
                 Contracts.Assert(toOutput.Length == 1);
                 var outCol = (OutColumn)toOutput[0];
-                return new IidSpikeEstimator(env,
+                return new MLContext().Transforms.IidSpikeEstimator(
                     outputNames[outCol],
+                    inputNames[outCol.Input],
                     _confidence,
                     _pvalueHistoryLength,
-                    inputNames[outCol.Input],
                     _side);
             }
         }
@@ -140,7 +138,7 @@ public static Vector<double> IidSpikeDetect(
             this Scalar<float> input,
             int confidence,
             int pvalueHistoryLength,
-            IidBase.AnomalySide side = IidBase.AnomalySide.TwoSided
+            AnomalySide side = AnomalySide.TwoSided
             ) => new OutColumn(input, confidence, pvalueHistoryLength, side);
     }
 
@@ -158,8 +156,8 @@ public OutColumn(Scalar<float> input,
                 int changeHistoryLength,
                 int trainingWindowSize,
                 int seasonalityWindowSize,
-                ErrorFunctionUtils.ErrorFunction errorFunction,
-                SsaBase.MartingaleType martingale,
+                ErrorFunction errorFunction,
+                MartingaleType martingale,
                 double eps)
                 : base(new Reconciler(confidence, changeHistoryLength, trainingWindowSize, seasonalityWindowSize, errorFunction, martingale, eps), input)
             {
@@ -173,8 +171,8 @@ private sealed class Reconciler : EstimatorReconciler
             private readonly int _changeHistoryLength;
             private readonly int _trainingWindowSize;
             private readonly int _seasonalityWindowSize;
-            private readonly ErrorFunctionUtils.ErrorFunction _errorFunction;
-            private readonly SsaBase.MartingaleType _martingale;
+            private readonly ErrorFunction _errorFunction;
+            private readonly MartingaleType _martingale;
             private readonly double _eps;
 
             public Reconciler(
@@ -182,8 +180,8 @@ public Reconciler(
                 int changeHistoryLength,
                 int trainingWindowSize,
                 int seasonalityWindowSize,
-                ErrorFunctionUtils.ErrorFunction errorFunction,
-                SsaBase.MartingaleType martingale,
+                ErrorFunction errorFunction,
+                MartingaleType martingale,
                 double eps)
             {
                 _confidence = confidence;
@@ -203,13 +201,13 @@ public override IEstimator<ITransformer> Reconcile(IHostEnvironment env,
             {
                 Contracts.Assert(toOutput.Length == 1);
                 var outCol = (OutColumn)toOutput[0];
-                return new SsaChangePointEstimator(env,
+                return new MLContext().Transforms.SsaChangePointEstimator(
                     outputNames[outCol],
+                    inputNames[outCol.Input],
                     _confidence,
                     _changeHistoryLength,
                     _trainingWindowSize,
                     _seasonalityWindowSize,
-                    inputNames[outCol.Input],
                     _errorFunction,
                     _martingale,
                     _eps);
@@ -225,8 +223,8 @@ public static Vector<double> SsaChangePointDetect(
             int changeHistoryLength,
             int trainingWindowSize,
             int seasonalityWindowSize,
-            ErrorFunctionUtils.ErrorFunction errorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference,
-            SsaBase.MartingaleType martingale = SsaBase.MartingaleType.Power,
+            ErrorFunction errorFunction = ErrorFunction.SignedDifference,
+            MartingaleType martingale = MartingaleType.Power,
             double eps = 0.1) => new OutColumn(input, confidence, changeHistoryLength, trainingWindowSize, seasonalityWindowSize, errorFunction, martingale, eps);
     }
 
@@ -244,8 +242,8 @@ public OutColumn(Scalar<float> input,
                 int pvalueHistoryLength,
                 int trainingWindowSize,
                 int seasonalityWindowSize,
-                SsaBase.AnomalySide side,
-                ErrorFunctionUtils.ErrorFunction errorFunction)
+                AnomalySide side,
+                ErrorFunction errorFunction)
                 : base(new Reconciler(confidence, pvalueHistoryLength, trainingWindowSize, seasonalityWindowSize, side, errorFunction), input)
             {
                 Input = input;
@@ -258,16 +256,16 @@ private sealed class Reconciler : EstimatorReconciler
             private readonly int _pvalueHistoryLength;
             private readonly int _trainingWindowSize;
             private readonly int _seasonalityWindowSize;
-            private readonly SsaBase.AnomalySide _side;
-            private readonly ErrorFunctionUtils.ErrorFunction _errorFunction;
+            private readonly AnomalySide _side;
+            private readonly ErrorFunction _errorFunction;
 
             public Reconciler(
                 int confidence,
                 int pvalueHistoryLength,
                 int trainingWindowSize,
                 int seasonalityWindowSize,
-                SsaBase.AnomalySide side,
-                ErrorFunctionUtils.ErrorFunction errorFunction)
+                AnomalySide side,
+                ErrorFunction errorFunction)
             {
                 _confidence = confidence;
                 _pvalueHistoryLength = pvalueHistoryLength;
@@ -285,13 +283,13 @@ public override IEstimator<ITransformer> Reconcile(IHostEnvironment env,
             {
                 Contracts.Assert(toOutput.Length == 1);
                 var outCol = (OutColumn)toOutput[0];
-                return new SsaSpikeEstimator(env,
+                return new MLContext().Transforms.SsaSpikeEstimator(
                     outputNames[outCol],
+                    inputNames[outCol.Input],
                     _confidence,
                     _pvalueHistoryLength,
                     _trainingWindowSize,
                     _seasonalityWindowSize,
-                    inputNames[outCol.Input],
                     _side,
                     _errorFunction);
             }
@@ -306,8 +304,8 @@ public static Vector<double> SsaSpikeDetect(
             int changeHistoryLength,
             int trainingWindowSize,
             int seasonalityWindowSize,
-            SsaBase.AnomalySide side = SsaBase.AnomalySide.TwoSided,
-            ErrorFunctionUtils.ErrorFunction errorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference
+            AnomalySide side = AnomalySide.TwoSided,
+            ErrorFunction errorFunction = ErrorFunction.SignedDifference
             ) => new OutColumn(input, confidence, changeHistoryLength, trainingWindowSize, seasonalityWindowSize, side, errorFunction);
 
     }
diff --git a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
index 5a5f40a0b8..f41298e41f 100644
--- a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
+++ b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
@@ -24,9 +24,9 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// This class implements basic Singular Spectrum Analysis (SSA) model for modeling univariate time-series.
     /// For the details of the model, refer to http://arxiv.org/pdf/1206.6910.pdf.
     /// </summary>
-    public sealed class AdaptiveSingularSpectrumSequenceModeler : SequenceModelerBase<Single, Single>
+    internal sealed class AdaptiveSingularSpectrumSequenceModeler : SequenceModelerBase<Single, Single>
     {
-        public const string LoaderSignature = "SSAModel";
+        internal const string LoaderSignature = "SSAModel";
 
         public enum RankSelectionMethod
         {
@@ -35,7 +35,7 @@ public enum RankSelectionMethod
             Fast
         }
 
-        public sealed class SsaForecastResult : ForecastResultBase<Single>
+        internal sealed class SsaForecastResult : ForecastResultBase<Single>
         {
             public VBuffer<Single> ForecastStandardDeviation;
             public VBuffer<Single> UpperBound;
@@ -1517,7 +1517,7 @@ internal override SequenceModelerBase<Single, Single> Clone()
         /// </summary>
         /// <param name="forecast">The input forecast object</param>
         /// <param name="confidenceLevel">The confidence level in [0, 1)</param>
-        public static void ComputeForecastIntervals(ref SsaForecastResult forecast, Single confidenceLevel = 0.95f)
+        internal static void ComputeForecastIntervals(ref SsaForecastResult forecast, Single confidenceLevel = 0.95f)
         {
             Contracts.CheckParam(0 <= confidenceLevel && confidenceLevel < 1, nameof(confidenceLevel), "The confidence level must be in [0, 1).");
             Contracts.CheckValue(forecast, nameof(forecast));
diff --git a/src/Microsoft.ML.TimeSeries/EigenUtils.cs b/src/Microsoft.ML.TimeSeries/EigenUtils.cs
index 8d0b6794e5..98be26f316 100644
--- a/src/Microsoft.ML.TimeSeries/EigenUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/EigenUtils.cs
@@ -11,7 +11,7 @@
 namespace Microsoft.ML.TimeSeriesProcessing
 {
     //REVIEW: improve perf with SSE and Multithreading
-    public static class EigenUtils
+    internal static class EigenUtils
     {
         //Compute the Eigen-decomposition of a symmetric matrix
         //REVIEW: use matrix/vector operations, not Array Math
diff --git a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
index a7a183cd63..dae1fae4f5 100644
--- a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
@@ -22,13 +22,14 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// <summary>
     /// ExponentialAverageTransform is a weighted average of the values: ExpAvg(y_t) = a * y_t + (1-a) * ExpAvg(y_(t-1)).
     /// </summary>
-    public sealed class ExponentialAverageTransform : SequentialTransformBase<Single, Single, ExponentialAverageTransform.State>
+    internal sealed class ExponentialAverageTransform : SequentialTransformBase<Single, Single, ExponentialAverageTransform.State>
     {
         public const string Summary = "Applies a Exponential average on a time series.";
         public const string LoaderSignature = "ExpAverageTransform";
         public const string UserName = "Exponential Average Transform";
         public const string ShortName = "ExpAvg";
 
+#pragma warning disable 0649
         public sealed class Arguments : TransformInputBase
         {
             [Argument(ArgumentType.Required, HelpText = "The name of the source column", ShortName = "src",
@@ -43,6 +44,7 @@ public sealed class Arguments : TransformInputBase
                 ShortName = "d", SortOrder = 4)]
             public Single Decay = 0.9f;
         }
+#pragma warning restore 0649
 
         private static VersionInfo GetVersionInfo()
         {
diff --git a/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs b/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs
new file mode 100644
index 0000000000..4090a26ebb
--- /dev/null
+++ b/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs
@@ -0,0 +1,87 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.ML.Data;
+using Microsoft.ML.TimeSeriesProcessing;
+
+namespace Microsoft.ML
+{
+    public static class TimeSeriesCatalog
+    {
+        /// <summary>
+        /// Create a new instance of <see cref="IidChangePointEstimator"/>
+        /// </summary>
+        /// <param name="catalog">The transform's catalog.</param>
+        /// <param name="outputColumnName">Name of the column resulting from the transformation of <paramref name="inputColumnName"/>.
+        /// Column is a vector of type double and size 4. The vector contains Alert, Raw Score, P-Value and Martingale score as first four values.</param>
+        /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.</param>
+        /// <param name="confidence">The confidence for change point detection in the range [0, 100].</param>
+        /// <param name="changeHistoryLength">The length of the sliding window on p-values for computing the martingale score.</param>
+        /// <param name="martingale">The martingale used for scoring.</param>
+        /// <param name="eps">The epsilon parameter for the Power martingale.</param>
+        public static IidChangePointEstimator IidChangePointEstimator(this TransformsCatalog catalog, string outputColumnName, string inputColumnName,
+            int confidence, int changeHistoryLength, MartingaleType martingale = MartingaleType.Power, double eps = 0.1)
+            => new IidChangePointEstimator(CatalogUtils.GetEnvironment(catalog), outputColumnName, confidence, changeHistoryLength, inputColumnName, martingale, eps);
+
+        /// <summary>
+        /// Create a new instance of <see cref="IidSpikeEstimator"/>
+        /// </summary>
+        /// <param name="catalog">The transform's catalog.</param>
+        /// <param name="outputColumnName">Name of the column resulting from the transformation of <paramref name="inputColumnName"/></param>.
+        /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.</param>
+        /// <param name="confidence">The confidence for spike detection in the range [0, 100].</param>
+        /// <param name="pvalueHistoryLength">The size of the sliding window for computing the p-value.</param>
+        /// <param name="side">The argument that determines whether to detect positive or negative anomalies, or both.</param>
+        public static IidSpikeEstimator IidSpikeEstimator(this TransformsCatalog catalog, string outputColumnName, string inputColumnName,
+             int confidence, int pvalueHistoryLength, AnomalySide side = AnomalySide.TwoSided)
+            => new IidSpikeEstimator(CatalogUtils.GetEnvironment(catalog), outputColumnName, confidence, pvalueHistoryLength, inputColumnName, side);
+
+        /// <summary>
+        /// Create a new instance of <see cref="SsaChangePointEstimator"/>
+        /// </summary>
+        /// <param name="catalog">The transform's catalog.</param>
+        /// <param name="outputColumnName">Name of the column resulting from the transformation of <paramref name="inputColumnName"/>.
+        /// Column is a vector of type double and size 4. The vector contains Alert, Raw Score, P-Value and Martingale score as first four values.</param>
+        /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.</param>
+        /// <param name="confidence">The confidence for change point detection in the range [0, 100].</param>
+        /// <param name="trainingWindowSize">The number of points from the beginning of the sequence used for training.</param>
+        /// <param name="changeHistoryLength">The size of the sliding window for computing the p-value.</param>
+        /// <param name="seasonalityWindowSize">An upper bound on the largest relevant seasonality in the input time-series.</param>
+        /// <param name="errorFunction">The function used to compute the error between the expected and the observed value.</param>
+        /// <param name="martingale">The martingale used for scoring.</param>
+        /// <param name="eps">The epsilon parameter for the Power martingale.</param>
+        public static SsaChangePointEstimator SsaChangePointEstimator(this TransformsCatalog catalog, string outputColumnName, string inputColumnName,
+            int confidence, int changeHistoryLength, int trainingWindowSize, int seasonalityWindowSize, ErrorFunction errorFunction = ErrorFunction.SignedDifference,
+            MartingaleType martingale = MartingaleType.Power, double eps = 0.1)
+            => new SsaChangePointEstimator(CatalogUtils.GetEnvironment(catalog), new SsaChangePointDetector.Options
+            {
+                Name = outputColumnName,
+                Source = inputColumnName ?? outputColumnName,
+                Confidence = confidence,
+                ChangeHistoryLength = changeHistoryLength,
+                TrainingWindowSize = trainingWindowSize,
+                SeasonalWindowSize = seasonalityWindowSize,
+                Martingale = martingale,
+                PowerMartingaleEpsilon = eps,
+                ErrorFunction = errorFunction
+            });
+
+        /// <summary>
+        /// Create a new instance of <see cref="SsaSpikeEstimator"/>
+        /// </summary>
+        /// <param name="catalog">The transform's catalog.</param>
+        /// <param name="outputColumnName">Name of the column resulting from the transformation of <paramref name="inputColumnName"/>.</param>
+        /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.
+        /// <param name="confidence">The confidence for spike detection in the range [0, 100].</param>
+        /// <param name="pvalueHistoryLength">The size of the sliding window for computing the p-value.</param>
+        /// <param name="trainingWindowSize">The number of points from the beginning of the sequence used for training.</param>
+        /// <param name="seasonalityWindowSize">An upper bound on the largest relevant seasonality in the input time-series.</param>
+        /// The vector contains Alert, Raw Score, P-Value as first three values.</param>
+        /// <param name="side">The argument that determines whether to detect positive or negative anomalies, or both.</param>
+        /// <param name="errorFunction">The function used to compute the error between the expected and the observed value.</param>
+        public static SsaSpikeEstimator SsaSpikeEstimator(this TransformsCatalog catalog, string outputColumnName, string inputColumnName, int confidence, int pvalueHistoryLength,
+            int trainingWindowSize, int seasonalityWindowSize, AnomalySide side = AnomalySide.TwoSided, ErrorFunction errorFunction = ErrorFunction.SignedDifference)
+            => new SsaSpikeEstimator(CatalogUtils.GetEnvironment(catalog), outputColumnName, confidence, pvalueHistoryLength, trainingWindowSize, seasonalityWindowSize, inputColumnName, side, errorFunction);
+    }
+}
diff --git a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
index f552ed2860..4773fac099 100644
--- a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
@@ -5,6 +5,7 @@
 using System;
 using System.IO;
 using Microsoft.Data.DataView;
+using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
@@ -13,99 +14,185 @@
 namespace Microsoft.ML.TimeSeriesProcessing
 {
     /// <summary>
-    /// This transform computes the p-values and martingale scores for a supposedly i.i.d input sequence of floats. In other words, it assumes
+    /// The is the wrapper to <see cref="IidAnomalyDetectionBase"/> that computes the p-values and martingale scores for a supposedly i.i.d input sequence of floats. In other words, it assumes
     /// the input sequence represents the raw anomaly score which might have been computed via another process.
     /// </summary>
-    public abstract class IidAnomalyDetectionBase : SequentialAnomalyDetectionTransformBase<Single, IidAnomalyDetectionBase.State>
+    public class IidAnomalyDetectionBaseWrapper : IStatefulTransformer, ICanSaveModel
     {
-        public IidAnomalyDetectionBase(ArgumentsBase args, string name, IHostEnvironment env)
-            : base(args, name, env)
+        /// <summary>
+        /// Whether a call to <see cref="GetRowToRowMapper(Schema)"/> should succeed, on an
+        /// appropriate schema.
+        /// </summary>
+        public bool IsRowToRowMapper => InternalTransform.IsRowToRowMapper;
+
+        /// <summary>
+        /// Creates a clone of the transfomer. Used for taking the snapshot of the state.
+        /// </summary>
+        /// <returns></returns>
+        IStatefulTransformer IStatefulTransformer.Clone() => InternalTransform.Clone();
+
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
+        public Schema GetOutputSchema(Schema inputSchema) => InternalTransform.GetOutputSchema(inputSchema);
+
+        /// <summary>
+        /// Constructs a row-to-row mapper based on an input schema. If <see cref="IsRowToRowMapper"/>
+        /// is <c>false</c>, then an exception should be thrown. If the input schema is in any way
+        /// unsuitable for constructing the mapper, an exception should likewise be thrown.
+        /// </summary>
+        /// <param name="inputSchema">The input schema for which we should get the mapper.</param>
+        /// <returns>The row to row mapper.</returns>
+        public IRowToRowMapper GetRowToRowMapper(Schema inputSchema) => InternalTransform.GetRowToRowMapper(inputSchema);
+
+        /// <summary>
+        /// Same as <see cref="ITransformer.GetRowToRowMapper(Schema)"/> but also supports mechanism to save the state.
+        /// </summary>
+        /// <param name="inputSchema">The input schema for which we should get the mapper.</param>
+        /// <returns>The row to row mapper.</returns>
+        public IRowToRowMapper GetStatefulRowToRowMapper(Schema inputSchema) => ((IStatefulTransformer)InternalTransform).GetStatefulRowToRowMapper(inputSchema);
+
+        /// <summary>
+        /// Take the data in, make transformations, output the data.
+        /// Note that <see cref="IDataView"/>'s are lazy, so no actual transformations happen here, just schema validation.
+        /// </summary>
+        public IDataView Transform(IDataView input) => InternalTransform.Transform(input);
+
+        /// <summary>
+        /// For saving a model into a repository.
+        /// </summary>
+        public virtual void Save(ModelSaveContext ctx)
         {
-            InitialWindowSize = 0;
-            StateRef = new State();
-            StateRef.InitState(WindowSize, InitialWindowSize, this, Host);
+            InternalTransform.SaveThis(ctx);
         }
 
-        public IidAnomalyDetectionBase(IHostEnvironment env, ModelLoadContext ctx, string name)
-            : base(env, ctx, name)
-        {
-            Host.CheckDecode(InitialWindowSize == 0);
-            StateRef = new State(ctx.Reader);
-            StateRef.InitState(this, Host);
-        }
-
-        public override Schema GetOutputSchema(Schema inputSchema)
-        {
-            Host.CheckValue(inputSchema, nameof(inputSchema));
+        /// <summary>
+        /// Creates a row mapper from Schema.
+        /// </summary>
+        internal IStatefulRowMapper MakeRowMapper(Schema schema) => InternalTransform.MakeRowMapper(schema);
 
-            if (!inputSchema.TryGetColumnIndex(InputColumnName, out var col))
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName);
+        /// <summary>
+        /// Creates an IDataTransform from an IDataView.
+        /// </summary>
+        internal IDataTransform MakeDataTransform(IDataView input) => InternalTransform.MakeDataTransform(input);
 
-            var colType = inputSchema[col].Type;
-            if (colType != NumberType.R4)
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName, "float", colType.ToString());
+        internal IidAnomalyDetectionBase InternalTransform;
 
-            return Transform(new EmptyDataView(Host, inputSchema)).Schema;
+        internal IidAnomalyDetectionBaseWrapper(ArgumentsBase args, string name, IHostEnvironment env)
+        {
+            InternalTransform = new IidAnomalyDetectionBase(args, name, env, this);
         }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
+        internal IidAnomalyDetectionBaseWrapper(IHostEnvironment env, ModelLoadContext ctx, string name)
         {
-            ctx.CheckAtModel();
-            Host.Assert(InitialWindowSize == 0);
-            base.SaveModel(ctx);
-
-            // *** Binary format ***
-            // <base>
-            // State: StateRef
-            StateRef.Save(ctx.Writer);
+            InternalTransform = new IidAnomalyDetectionBase(env, ctx, name, this);
         }
 
-        public sealed class State : AnomalyDetectionStateBase
+        /// <summary>
+        /// This transform computes the p-values and martingale scores for a supposedly i.i.d input sequence of floats. In other words, it assumes
+        /// the input sequence represents the raw anomaly score which might have been computed via another process.
+        /// </summary>
+        internal class IidAnomalyDetectionBase : SequentialAnomalyDetectionTransformBase<Single, IidAnomalyDetectionBase.State>
         {
-            public State()
-            {
-            }
+            internal IidAnomalyDetectionBaseWrapper Parent;
 
-            internal State(BinaryReader reader) : base(reader)
+            public IidAnomalyDetectionBase(ArgumentsBase args, string name, IHostEnvironment env, IidAnomalyDetectionBaseWrapper parent)
+                : base(args, name, env)
             {
-                WindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
-                InitialWindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                InitialWindowSize = 0;
+                StateRef = new State();
+                StateRef.InitState(WindowSize, InitialWindowSize, this, Host);
+                Parent = parent;
             }
 
-            internal override void Save(BinaryWriter writer)
+            public IidAnomalyDetectionBase(IHostEnvironment env, ModelLoadContext ctx, string name, IidAnomalyDetectionBaseWrapper parent)
+                : base(env, ctx, name)
             {
-                base.Save(writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(WindowedBuffer, writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(InitialWindowedBuffer, writer);
+                Host.CheckDecode(InitialWindowSize == 0);
+                StateRef = new State(ctx.Reader);
+                StateRef.InitState(this, Host);
+                Parent = parent;
             }
 
-            private protected override void CloneCore(StateBase state)
+            public override Schema GetOutputSchema(Schema inputSchema)
             {
-                base.CloneCore(state);
-                Contracts.Assert(state is State);
-                var stateLocal = state as State;
-                stateLocal.WindowedBuffer = WindowedBuffer.Clone();
-                stateLocal.InitialWindowedBuffer = InitialWindowedBuffer.Clone();
-            }
+                Host.CheckValue(inputSchema, nameof(inputSchema));
 
-            private protected override void LearnStateFromDataCore(FixedSizeQueue<Single> data)
-            {
-                // This method is empty because there is no need for initial tuning for this transform.
+                if (!inputSchema.TryGetColumnIndex(InputColumnName, out var col))
+                    throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName);
+
+                var colType = inputSchema[col].Type;
+                if (colType != NumberType.R4)
+                    throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName, NumberType.R4.ToString(), colType.ToString());
+
+                return Transform(new EmptyDataView(Host, inputSchema)).Schema;
             }
 
-            private protected override void InitializeAnomalyDetector()
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
-                // This method is empty because there is no need for any extra initialization for this transform.
+                Parent.Save(ctx);
             }
 
-            private protected override double ComputeRawAnomalyScore(ref Single input, FixedSizeQueue<Single> windowedBuffer, long iteration)
+            internal void SaveThis(ModelSaveContext ctx)
             {
-                // This transform treats the input sequenence as the raw anomaly score.
-                return (double)input;
+                ctx.CheckAtModel();
+                Host.Assert(InitialWindowSize == 0);
+                base.SaveModel(ctx);
+
+                // *** Binary format ***
+                // <base>
+                // State: StateRef
+                StateRef.Save(ctx.Writer);
             }
 
-            public override void Consume(float value)
+            internal sealed class State : AnomalyDetectionStateBase
             {
+                public State()
+                {
+                }
+
+                internal State(BinaryReader reader) : base(reader)
+                {
+                    WindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                    InitialWindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                }
+
+                internal override void Save(BinaryWriter writer)
+                {
+                    base.Save(writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(WindowedBuffer, writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(InitialWindowedBuffer, writer);
+                }
+
+                private protected override void CloneCore(State state)
+                {
+                    base.CloneCore(state);
+                    Contracts.Assert(state is State);
+                    var stateLocal = state as State;
+                    stateLocal.WindowedBuffer = WindowedBuffer.Clone();
+                    stateLocal.InitialWindowedBuffer = InitialWindowedBuffer.Clone();
+                }
+
+                private protected override void LearnStateFromDataCore(FixedSizeQueue<Single> data)
+                {
+                    // This method is empty because there is no need for initial tuning for this transform.
+                }
+
+                private protected override void InitializeAnomalyDetector()
+                {
+                    // This method is empty because there is no need for any extra initialization for this transform.
+                }
+
+                private protected override double ComputeRawAnomalyScore(ref Single input, FixedSizeQueue<Single> windowedBuffer, long iteration)
+                {
+                    // This transform treats the input sequenence as the raw anomaly score.
+                    return (double)input;
+                }
+
+                public override void Consume(float value)
+                {
+                }
             }
         }
     }
diff --git a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
index 873b93a643..3984d6973d 100644
--- a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
@@ -14,9 +14,8 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.TimeSeries;
 using Microsoft.ML.TimeSeriesProcessing;
-using static Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<System.Single, Microsoft.ML.TimeSeriesProcessing.IidAnomalyDetectionBase.State>;
 
-[assembly: LoadableClass(IidChangePointDetector.Summary, typeof(IDataTransform), typeof(IidChangePointDetector), typeof(IidChangePointDetector.Arguments), typeof(SignatureDataTransform),
+[assembly: LoadableClass(IidChangePointDetector.Summary, typeof(IDataTransform), typeof(IidChangePointDetector), typeof(IidChangePointDetector.Options), typeof(SignatureDataTransform),
     IidChangePointDetector.UserName, IidChangePointDetector.LoaderSignature, IidChangePointDetector.ShortName)]
 
 [assembly: LoadableClass(IidChangePointDetector.Summary, typeof(IDataTransform), typeof(IidChangePointDetector), null, typeof(SignatureLoadDataTransform),
@@ -33,14 +32,14 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// <summary>
     /// This class implements the change point detector transform for an i.i.d. sequence based on adaptive kernel density estimation and martingales.
     /// </summary>
-    public sealed class IidChangePointDetector : IidAnomalyDetectionBase
+    public sealed class IidChangePointDetector : IidAnomalyDetectionBaseWrapper, IStatefulTransformer
     {
         internal const string Summary = "This transform detects the change-points in an i.i.d. sequence using adaptive kernel density estimation and martingales.";
-        public const string LoaderSignature = "IidChangePointDetector";
-        public const string UserName = "IID Change Point Detection";
-        public const string ShortName = "ichgpnt";
+        internal const string LoaderSignature = "IidChangePointDetector";
+        internal const string UserName = "IID Change Point Detection";
+        internal const string ShortName = "ichgpnt";
 
-        public sealed class Arguments : TransformInputBase
+        internal sealed class Options : TransformInputBase
         {
             [Argument(ArgumentType.Required, HelpText = "The name of the source column.", ShortName = "src",
                 SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
@@ -59,7 +58,7 @@ public sealed class Arguments : TransformInputBase
             public double Confidence = 95;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The martingale used for scoring.", ShortName = "mart", SortOrder = 103)]
-            public MartingaleType Martingale = SequentialAnomalyDetectionTransformBase<float, State>.MartingaleType.Power;
+            public MartingaleType Martingale = MartingaleType.Power;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The epsilon parameter for the Power martingale.",
                 ShortName = "eps", SortOrder = 104)]
@@ -68,27 +67,27 @@ public sealed class Arguments : TransformInputBase
 
         private sealed class BaseArguments : ArgumentsBase
         {
-            public BaseArguments(Arguments args)
+            public BaseArguments(Options options)
             {
-                Source = args.Source;
-                Name = args.Name;
-                Side = SequentialAnomalyDetectionTransformBase<float, State>.AnomalySide.TwoSided;
-                WindowSize = args.ChangeHistoryLength;
-                Martingale = args.Martingale;
-                PowerMartingaleEpsilon = args.PowerMartingaleEpsilon;
-                AlertOn = SequentialAnomalyDetectionTransformBase<float, State>.AlertingScore.MartingaleScore;
+                Source = options.Source;
+                Name = options.Name;
+                Side = AnomalySide.TwoSided;
+                WindowSize = options.ChangeHistoryLength;
+                Martingale = options.Martingale;
+                PowerMartingaleEpsilon = options.PowerMartingaleEpsilon;
+                AlertOn = AlertingScore.MartingaleScore;
             }
 
             public BaseArguments(IidChangePointDetector transform)
             {
-                Source = transform.InputColumnName;
-                Name = transform.OutputColumnName;
+                Source = transform.InternalTransform.InputColumnName;
+                Name = transform.InternalTransform.OutputColumnName;
                 Side = AnomalySide.TwoSided;
-                WindowSize = transform.WindowSize;
-                Martingale = transform.Martingale;
-                PowerMartingaleEpsilon = transform.PowerMartingaleEpsilon;
+                WindowSize = transform.InternalTransform.WindowSize;
+                Martingale = transform.InternalTransform.Martingale;
+                PowerMartingaleEpsilon = transform.InternalTransform.PowerMartingaleEpsilon;
                 AlertOn = AlertingScore.MartingaleScore;
-                AlertThreshold = transform.AlertThreshold;
+                AlertThreshold = transform.InternalTransform.AlertThreshold;
             }
         }
 
@@ -103,39 +102,39 @@ private static VersionInfo GetVersionInfo()
         }
 
         // Factory method for SignatureDataTransform.
-        private static IDataTransform Create(IHostEnvironment env, Arguments args, IDataView input)
+        private static IDataTransform Create(IHostEnvironment env, Options options, IDataView input)
         {
             Contracts.CheckValue(env, nameof(env));
-            env.CheckValue(args, nameof(args));
+            env.CheckValue(options, nameof(options));
             env.CheckValue(input, nameof(input));
 
-            return new IidChangePointDetector(env, args).MakeDataTransform(input);
+            return new IidChangePointDetector(env, options).MakeDataTransform(input);
         }
 
-        internal override IStatefulTransformer Clone()
+        IStatefulTransformer IStatefulTransformer.Clone()
         {
             var clone = (IidChangePointDetector)MemberwiseClone();
-            clone.StateRef = (State)clone.StateRef.Clone();
-            clone.StateRef.InitState(clone, Host);
+            clone.InternalTransform.StateRef = (IidAnomalyDetectionBase.State)clone.InternalTransform.StateRef.Clone();
+            clone.InternalTransform.StateRef.InitState(clone.InternalTransform, InternalTransform.Host);
             return clone;
         }
 
-        internal IidChangePointDetector(IHostEnvironment env, Arguments args)
-            : base(new BaseArguments(args), LoaderSignature, env)
+        internal IidChangePointDetector(IHostEnvironment env, Options options)
+            : base(new BaseArguments(options), LoaderSignature, env)
         {
-            switch (Martingale)
+            switch (InternalTransform.Martingale)
             {
                 case MartingaleType.None:
-                    AlertThreshold = Double.MaxValue;
+                    InternalTransform.AlertThreshold = Double.MaxValue;
                     break;
                 case MartingaleType.Power:
-                    AlertThreshold = Math.Exp(WindowSize * LogPowerMartigaleBettingFunc(1 - args.Confidence / 100, PowerMartingaleEpsilon));
+                    InternalTransform.AlertThreshold = Math.Exp(InternalTransform.WindowSize * InternalTransform.LogPowerMartigaleBettingFunc(1 - options.Confidence / 100, InternalTransform.PowerMartingaleEpsilon));
                     break;
                 case MartingaleType.Mixture:
-                    AlertThreshold = Math.Exp(WindowSize * LogMixtureMartigaleBettingFunc(1 - args.Confidence / 100));
+                    InternalTransform.AlertThreshold = Math.Exp(InternalTransform.WindowSize * InternalTransform.LogMixtureMartigaleBettingFunc(1 - options.Confidence / 100));
                     break;
                 default:
-                    throw Host.ExceptParam(nameof(args.Martingale),
+                    throw InternalTransform.Host.ExceptParam(nameof(options.Martingale),
                         "The martingale type can be only (0) None, (1) Power or (2) Mixture.");
             }
         }
@@ -166,8 +165,8 @@ internal IidChangePointDetector(IHostEnvironment env, ModelLoadContext ctx)
             // *** Binary format ***
             // <base>
 
-            Host.CheckDecode(ThresholdScore == AlertingScore.MartingaleScore);
-            Host.CheckDecode(Side == AnomalySide.TwoSided);
+            InternalTransform.Host.CheckDecode(InternalTransform.ThresholdScore == AlertingScore.MartingaleScore);
+            InternalTransform.Host.CheckDecode(InternalTransform.Side == AnomalySide.TwoSided);
         }
 
         private IidChangePointDetector(IHostEnvironment env, IidChangePointDetector transform)
@@ -175,19 +174,19 @@ private IidChangePointDetector(IHostEnvironment env, IidChangePointDetector tran
         {
         }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
+        public override void Save(ModelSaveContext ctx)
         {
-            Host.CheckValue(ctx, nameof(ctx));
+            InternalTransform.Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
 
-            Host.Assert(ThresholdScore == AlertingScore.MartingaleScore);
-            Host.Assert(Side == AnomalySide.TwoSided);
+            InternalTransform.Host.Assert(InternalTransform.ThresholdScore == AlertingScore.MartingaleScore);
+            InternalTransform.Host.Assert(InternalTransform.Side == AnomalySide.TwoSided);
 
             // *** Binary format ***
             // <base>
 
-            base.SaveModel(ctx);
+            base.Save(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
@@ -219,10 +218,10 @@ public sealed class IidChangePointEstimator : TrivialEstimator<IidChangePointDet
         /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.</param>
         /// <param name="martingale">The martingale used for scoring.</param>
         /// <param name="eps">The epsilon parameter for the Power martingale.</param>
-        public IidChangePointEstimator(IHostEnvironment env, string outputColumnName, int confidence,
+        internal IidChangePointEstimator(IHostEnvironment env, string outputColumnName, int confidence,
             int changeHistoryLength, string inputColumnName, MartingaleType martingale = MartingaleType.Power, double eps = 0.1)
             : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(IidChangePointEstimator)),
-                new IidChangePointDetector(env, new IidChangePointDetector.Arguments
+                new IidChangePointDetector(env, new IidChangePointDetector.Options
                 {
                     Name = outputColumnName,
                     Source = inputColumnName ?? outputColumnName,
@@ -234,28 +233,32 @@ public IidChangePointEstimator(IHostEnvironment env, string outputColumnName, in
         {
         }
 
-        public IidChangePointEstimator(IHostEnvironment env, IidChangePointDetector.Arguments args)
+        internal IidChangePointEstimator(IHostEnvironment env, IidChangePointDetector.Options options)
             : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(IidChangePointEstimator)),
-                new IidChangePointDetector(env, args))
+                new IidChangePointDetector(env, options))
         {
         }
 
+        /// <summary>
+        /// Returns the <see cref="SchemaShape"/> of the schema which will be produced by the transformer.
+        /// Used for schema propagation and verification in a pipeline.
+        /// </summary>
         public override SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
             Host.CheckValue(inputSchema, nameof(inputSchema));
 
-            if (!inputSchema.TryFindColumn(Transformer.InputColumnName, out var col))
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InputColumnName);
+            if (!inputSchema.TryFindColumn(Transformer.InternalTransform.InputColumnName, out var col))
+                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InternalTransform.InputColumnName);
             if (col.ItemType != NumberType.R4)
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InputColumnName, "float", col.GetTypeString());
+                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InternalTransform.InputColumnName, "float", col.GetTypeString());
 
             var metadata = new List<SchemaShape.Column>() {
                 new SchemaShape.Column(MetadataUtils.Kinds.SlotNames, SchemaShape.Column.VectorKind.Vector, TextType.Instance, false)
             };
             var resultDic = inputSchema.ToDictionary(x => x.Name);
 
-            resultDic[Transformer.OutputColumnName] = new SchemaShape.Column(
-                Transformer.OutputColumnName, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
+            resultDic[Transformer.InternalTransform.OutputColumnName] = new SchemaShape.Column(
+                Transformer.InternalTransform.OutputColumnName, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
 
             return new SchemaShape(resultDic.Values);
         }
diff --git a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
index a0930d110c..c6d06e66ae 100644
--- a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
@@ -13,9 +13,8 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.TimeSeries;
 using Microsoft.ML.TimeSeriesProcessing;
-using static Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<System.Single, Microsoft.ML.TimeSeriesProcessing.IidAnomalyDetectionBase.State>;
 
-[assembly: LoadableClass(IidSpikeDetector.Summary, typeof(IDataTransform), typeof(IidSpikeDetector), typeof(IidSpikeDetector.Arguments), typeof(SignatureDataTransform),
+[assembly: LoadableClass(IidSpikeDetector.Summary, typeof(IDataTransform), typeof(IidSpikeDetector), typeof(IidSpikeDetector.Options), typeof(SignatureDataTransform),
     IidSpikeDetector.UserName, IidSpikeDetector.LoaderSignature, IidSpikeDetector.ShortName)]
 
 [assembly: LoadableClass(IidSpikeDetector.Summary, typeof(IDataTransform), typeof(IidSpikeDetector), null, typeof(SignatureLoadDataTransform),
@@ -32,14 +31,14 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// <summary>
     /// This class implements the spike detector transform for an i.i.d. sequence based on adaptive kernel density estimation.
     /// </summary>
-    public sealed class IidSpikeDetector : IidAnomalyDetectionBase
+    public sealed class IidSpikeDetector : IidAnomalyDetectionBaseWrapper, IStatefulTransformer
     {
         internal const string Summary = "This transform detects the spikes in a i.i.d. sequence using adaptive kernel density estimation.";
-        public const string LoaderSignature = "IidSpikeDetector";
-        public const string UserName = "IID Spike Detection";
-        public const string ShortName = "ispike";
+        internal const string LoaderSignature = "IidSpikeDetector";
+        internal const string UserName = "IID Spike Detection";
+        internal const string ShortName = "ispike";
 
-        public sealed class Arguments : TransformInputBase
+        internal sealed class Options : TransformInputBase
         {
             [Argument(ArgumentType.Required, HelpText = "The name of the source column.", ShortName = "src",
                 SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
@@ -64,24 +63,24 @@ public sealed class Arguments : TransformInputBase
 
         private sealed class BaseArguments : ArgumentsBase
         {
-            public BaseArguments(Arguments args)
+            public BaseArguments(Options options)
             {
-                Source = args.Source;
-                Name = args.Name;
-                Side = args.Side;
-                WindowSize = args.PvalueHistoryLength;
-                AlertThreshold = 1 - args.Confidence / 100;
-                AlertOn = SequentialAnomalyDetectionTransformBase<float, State>.AlertingScore.PValueScore;
+                Source = options.Source;
+                Name = options.Name;
+                Side = options.Side;
+                WindowSize = options.PvalueHistoryLength;
+                AlertThreshold = 1 - options.Confidence / 100;
+                AlertOn = AlertingScore.PValueScore;
                 Martingale = MartingaleType.None;
             }
 
             public BaseArguments(IidSpikeDetector transform)
             {
-                Source = transform.InputColumnName;
-                Name = transform.OutputColumnName;
-                Side = transform.Side;
-                WindowSize = transform.WindowSize;
-                AlertThreshold = transform.AlertThreshold;
+                Source = transform.InternalTransform.InputColumnName;
+                Name = transform.InternalTransform.OutputColumnName;
+                Side = transform.InternalTransform.Side;
+                WindowSize = transform.InternalTransform.WindowSize;
+                AlertThreshold = transform.InternalTransform.AlertThreshold;
                 AlertOn = AlertingScore.PValueScore;
                 Martingale = MartingaleType.None;
             }
@@ -99,25 +98,25 @@ private static VersionInfo GetVersionInfo()
         }
 
         // Factory method for SignatureDataTransform.
-        private static IDataTransform Create(IHostEnvironment env, Arguments args, IDataView input)
+        private static IDataTransform Create(IHostEnvironment env, Options options, IDataView input)
         {
             Contracts.CheckValue(env, nameof(env));
-            env.CheckValue(args, nameof(args));
+            env.CheckValue(options, nameof(options));
             env.CheckValue(input, nameof(input));
 
-            return new IidSpikeDetector(env, args).MakeDataTransform(input);
+            return new IidSpikeDetector(env, options).MakeDataTransform(input);
         }
 
-        internal override IStatefulTransformer Clone()
+        IStatefulTransformer IStatefulTransformer.Clone()
         {
             var clone = (IidSpikeDetector)MemberwiseClone();
-            clone.StateRef = (State)clone.StateRef.Clone();
-            clone.StateRef.InitState(clone, Host);
+            clone.InternalTransform.StateRef = (IidAnomalyDetectionBase.State)clone.InternalTransform.StateRef.Clone();
+            clone.InternalTransform.StateRef.InitState(clone.InternalTransform, InternalTransform.Host);
             return clone;
         }
 
-        internal IidSpikeDetector(IHostEnvironment env, Arguments args)
-            : base(new BaseArguments(args), LoaderSignature, env)
+        internal IidSpikeDetector(IHostEnvironment env, Options options)
+            : base(new BaseArguments(options), LoaderSignature, env)
         {
             // This constructor is empty.
         }
@@ -142,31 +141,32 @@ private static IidSpikeDetector Create(IHostEnvironment env, ModelLoadContext ct
             return new IidSpikeDetector(env, ctx);
         }
 
-        public IidSpikeDetector(IHostEnvironment env, ModelLoadContext ctx)
+        internal IidSpikeDetector(IHostEnvironment env, ModelLoadContext ctx)
             : base(env, ctx, LoaderSignature)
         {
             // *** Binary format ***
             // <base>
 
-            Host.CheckDecode(ThresholdScore == AlertingScore.PValueScore);
+            InternalTransform.Host.CheckDecode(InternalTransform.ThresholdScore == AlertingScore.PValueScore);
         }
+
         private IidSpikeDetector(IHostEnvironment env, IidSpikeDetector transform)
            : base(new BaseArguments(transform), LoaderSignature, env)
         {
         }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
+        public override void Save(ModelSaveContext ctx)
         {
-            Host.CheckValue(ctx, nameof(ctx));
+            InternalTransform.Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
 
-            Host.Assert(ThresholdScore == AlertingScore.PValueScore);
+            InternalTransform.Host.Assert(InternalTransform.ThresholdScore == AlertingScore.PValueScore);
 
             // *** Binary format ***
             // <base>
 
-            base.SaveModel(ctx);
+            base.Save(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
@@ -197,9 +197,9 @@ public sealed class IidSpikeEstimator : TrivialEstimator<IidSpikeDetector>
         /// <param name="pvalueHistoryLength">The size of the sliding window for computing the p-value.</param>
         /// <param name="inputColumnName">Name of column to transform. If set to <see langword="null"/>, the value of the <paramref name="outputColumnName"/> will be used as source.</param>
         /// <param name="side">The argument that determines whether to detect positive or negative anomalies, or both.</param>
-        public IidSpikeEstimator(IHostEnvironment env, string outputColumnName, int confidence, int pvalueHistoryLength, string inputColumnName, AnomalySide side = AnomalySide.TwoSided)
+        internal IidSpikeEstimator(IHostEnvironment env, string outputColumnName, int confidence, int pvalueHistoryLength, string inputColumnName, AnomalySide side = AnomalySide.TwoSided)
             : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(IidSpikeDetector)),
-                new IidSpikeDetector(env, new IidSpikeDetector.Arguments
+                new IidSpikeDetector(env, new IidSpikeDetector.Options
                 {
                     Name = outputColumnName,
                     Source = inputColumnName,
@@ -210,26 +210,30 @@ public IidSpikeEstimator(IHostEnvironment env, string outputColumnName, int conf
         {
         }
 
-        public IidSpikeEstimator(IHostEnvironment env, IidSpikeDetector.Arguments args)
-            : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(IidSpikeEstimator)), new IidSpikeDetector(env, args))
+        internal IidSpikeEstimator(IHostEnvironment env, IidSpikeDetector.Options options)
+            : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(IidSpikeEstimator)), new IidSpikeDetector(env, options))
         {
         }
 
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
         public override SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
             Host.CheckValue(inputSchema, nameof(inputSchema));
 
-            if (!inputSchema.TryFindColumn(Transformer.InputColumnName, out var col))
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InputColumnName);
+            if (!inputSchema.TryFindColumn(Transformer.InternalTransform.InputColumnName, out var col))
+                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InternalTransform.InputColumnName);
             if (col.ItemType != NumberType.R4)
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InputColumnName, "float", col.GetTypeString());
+                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", Transformer.InternalTransform.InputColumnName, "float", col.GetTypeString());
 
             var metadata = new List<SchemaShape.Column>() {
                 new SchemaShape.Column(MetadataUtils.Kinds.SlotNames, SchemaShape.Column.VectorKind.Vector, TextType.Instance, false)
             };
             var resultDic = inputSchema.ToDictionary(x => x.Name);
-            resultDic[Transformer.OutputColumnName] = new SchemaShape.Column(
-                Transformer.OutputColumnName, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
+            resultDic[Transformer.InternalTransform.OutputColumnName] = new SchemaShape.Column(
+                Transformer.InternalTransform.OutputColumnName, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
 
             return new SchemaShape(resultDic.Values);
         }
diff --git a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
index 8cea2e751f..2c51fa13f5 100644
--- a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
@@ -23,7 +23,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// MovingAverageTransform is a weighted average of the values in
     /// the sliding window.
     /// </summary>
-    public sealed class MovingAverageTransform : SequentialTransformBase<Single, Single, MovingAverageTransform.State>
+    internal sealed class MovingAverageTransform : SequentialTransformBase<Single, Single, MovingAverageTransform.State>
     {
         public const string Summary = "Applies a moving average on a time series. Only finite values are taken into account.";
         public const string LoaderSignature = "MovingAverageTransform";
diff --git a/src/Microsoft.ML.TimeSeries/PValueTransform.cs b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
index b22d7fcc0c..a1723ef162 100644
--- a/src/Microsoft.ML.TimeSeries/PValueTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
@@ -23,7 +23,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// PValueTransform is a sequential transform that computes the empirical p-value of the current value in the series based on the other values in
     /// the sliding window.
     /// </summary>
-    public sealed class PValueTransform : SequentialTransformBase<Single, Single, PValueTransform.State>
+    internal sealed class PValueTransform : SequentialTransformBase<Single, Single, PValueTransform.State>
     {
         internal const string Summary = "This P-Value transform calculates the p-value of the current input in the sequence with regard to the values in the sliding window.";
         public const string LoaderSignature = "PValueTransform";
diff --git a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
index 18a12ed3be..bfac8ad93a 100644
--- a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
@@ -23,7 +23,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// PercentileThresholdTransform is a sequential transform that decides whether the current value of the time-series belongs to the 'percentile' % of the top values in
     /// the sliding window. The output of the transform will be a boolean flag.
     /// </summary>
-    public sealed class PercentileThresholdTransform : SequentialTransformBase<Single, bool, PercentileThresholdTransform.State>
+    internal sealed class PercentileThresholdTransform : SequentialTransformBase<Single, bool, PercentileThresholdTransform.State>
     {
         public const string Summary = "Detects the values of time-series that are in the top percentile of the sliding window.";
         public const string LoaderSignature = "PercentThrTransform";
diff --git a/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs b/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
index 094984dcb2..19b1d7f97d 100644
--- a/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
@@ -10,7 +10,7 @@
 
 namespace Microsoft.ML.TimeSeriesProcessing
 {
-    public static class PolynomialUtils
+    internal static class PolynomialUtils
     {
         // Part 1: Computing the polynomial real and complex roots from its real coefficients
 
diff --git a/src/Microsoft.ML.TimeSeries/PredictionFunction.cs b/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
index e0c352e632..0e97713247 100644
--- a/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
+++ b/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
@@ -18,8 +18,17 @@ internal interface IStatefulRowToRowMapper : IRowToRowMapper
 
     internal interface IStatefulTransformer : ITransformer
     {
+        /// <summary>
+        /// Same as <see cref="ITransformer.GetRowToRowMapper(Schema)"/> but also supports mechanism to save the state.
+        /// </summary>
+        /// <param name="inputSchema">The input schema for which we should get the mapper.</param>
+        /// <returns>The row to row mapper.</returns>
         IRowToRowMapper GetStatefulRowToRowMapper(Schema inputSchema);
 
+        /// <summary>
+        /// Creates a clone of the transfomer. Used for taking the snapshot of the state.
+        /// </summary>
+        /// <returns></returns>
         IStatefulTransformer Clone();
     }
 
@@ -90,6 +99,10 @@ private static ITransformer CloneTransformers(ITransformer transformer)
                 return transformer is IStatefulTransformer ? ((IStatefulTransformer)transformer).Clone() : transformer;
         }
 
+        /// <summary>
+        /// Contructor for creating time series specific prediction engine. It allows update the time series model to be updated with the observations
+        /// seen at prediction time via <see cref="CheckPoint(IHostEnvironment, string)"/>
+        /// </summary>
         public TimeSeriesPredictionFunction(IHostEnvironment env, ITransformer transformer, bool ignoreMissingColumns,
             SchemaDefinition inputSchemaDefinition = null, SchemaDefinition outputSchemaDefinition = null) :
             base(env, CloneTransformers(transformer), ignoreMissingColumns, inputSchemaDefinition, outputSchemaDefinition)
@@ -200,7 +213,7 @@ private IRowToRowMapper GetRowToRowMapper(Schema inputSchema)
             return new CompositeRowToRowMapper(inputSchema, mappers);
         }
 
-        protected override Func<Schema, IRowToRowMapper> TransformerChecker(IExceptionContext ectx, ITransformer transformer)
+        private protected override Func<Schema, IRowToRowMapper> TransformerChecker(IExceptionContext ectx, ITransformer transformer)
         {
             ectx.CheckValue(transformer, nameof(transformer));
             ectx.CheckParam(IsRowToRowMapper(transformer), nameof(transformer), "Must be a row to row mapper or " + nameof(IStatefulTransformer));
diff --git a/src/Microsoft.ML.TimeSeries/Properties/AssemblyInfo.cs b/src/Microsoft.ML.TimeSeries/Properties/AssemblyInfo.cs
index 56899c0981..a16f8e17fd 100644
--- a/src/Microsoft.ML.TimeSeries/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.ML.TimeSeries/Properties/AssemblyInfo.cs
@@ -5,4 +5,7 @@
 using System.Runtime.CompilerServices;
 using Microsoft.ML;
 
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.TimeSeries.Tests" + PublicKey.TestValue)]
 [assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Core.Tests" + PublicKey.TestValue)]
+
+[assembly: WantsToBeBestFriends]
diff --git a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
index ca113b9007..d643435704 100644
--- a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
@@ -12,7 +12,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// The base container class for the forecast result on a sequence of type <typeparamref name="T"/>.
     /// </summary>
     /// <typeparam name="T">The type of the elements in the sequence</typeparam>
-    public abstract class ForecastResultBase<T>
+    internal abstract class ForecastResultBase<T>
     {
         public VBuffer<T> PointForecast;
     }
@@ -22,7 +22,7 @@ public abstract class ForecastResultBase<T>
     /// </summary>
     /// <typeparam name="TInput">The type of the elements in the input sequence</typeparam>
     /// <typeparam name="TOutput">The type of the elements in the output sequence</typeparam>
-    public abstract class SequenceModelerBase<TInput, TOutput> : ICanSaveModel
+    internal abstract class SequenceModelerBase<TInput, TOutput> : ICanSaveModel
     {
         private protected SequenceModelerBase()
         {
diff --git a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
index e0d722a7a6..d9c3750cd6 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
@@ -16,678 +16,676 @@
 
 namespace Microsoft.ML.TimeSeriesProcessing
 {
-    // REVIEW: This base class and its children classes generate one output column of type VBuffer<Double> to output 3 different anomaly scores as well as
-    // the alert flag. Ideally these 4 output information should be put in four seaparate columns instead of one VBuffer<> column. However, this is not currently
-    // possible due to our design restriction. This must be fixed in the next version and will potentially affect the children classes.
-
     /// <summary>
-    /// The base class for sequential anomaly detection transforms that supports the p-value as well as the martingales scores computation from the sequence of
-    /// raw anomaly scores whose calculation is specified by the children classes. This class also provides mechanism for the threshold-based alerting on
-    /// the raw anomaly score, the p-value score or the martingale score. Currently, this class supports Power and Mixture martingales.
-    /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf
+    /// The type of the martingale.
     /// </summary>
-    /// <typeparam name="TInput">The type of the input sequence</typeparam>
-    /// <typeparam name="TState">The type of the state object for sequential anomaly detection. Must be a class inherited from AnomalyDetectionStateBase</typeparam>
-    public abstract class SequentialAnomalyDetectionTransformBase<TInput, TState> : SequentialTransformerBase<TInput, VBuffer<Double>, TState>
-        where TState : SequentialAnomalyDetectionTransformBase<TInput, TState>.AnomalyDetectionStateBase, new()
+    public enum MartingaleType : byte
     {
         /// <summary>
-        /// The type of the martingale.
+        /// (None) No martingale is used.
         /// </summary>
-        public enum MartingaleType : byte
-        {
-            /// <summary>
-            /// (None) No martingale is used.
-            /// </summary>
-            None,
-            /// <summary>
-            /// (Power) The Power martingale is used.
-            /// </summary>
-            Power,
-            /// <summary>
-            /// (Mixture) The Mixture martingale is used.
-            /// </summary>
-            Mixture
-        }
-
+        None,
         /// <summary>
-        /// The side of anomaly detection.
+        /// (Power) The Power martingale is used.
         /// </summary>
-        public enum AnomalySide : byte
-        {
-            /// <summary>
-            /// (Positive) Only positive anomalies are detected.
-            /// </summary>
-            Positive,
-            /// <summary>
-            /// (Negative) Only negative anomalies are detected.
-            /// </summary>
-            Negative,
-            /// <summary>
-            /// (TwoSided) Both positive and negative anomalies are detected.
-            /// </summary>
-            TwoSided
-        }
+        Power,
+        /// <summary>
+        /// (Mixture) The Mixture martingale is used.
+        /// </summary>
+        Mixture
+    }
 
+    /// <summary>
+    /// The side of anomaly detection.
+    /// </summary>
+    public enum AnomalySide : byte
+    {
         /// <summary>
-        /// The score that should be thresholded to generate alerts.
+        /// (Positive) Only positive anomalies are detected.
         /// </summary>
-        public enum AlertingScore : byte
-        {
-            /// <summary>
-            /// (RawScore) The raw anomaly score is thresholded.
-            /// </summary>
-            RawScore,
-            /// <summary>
-            /// (PValueScore) The p-value score is thresholded.
-            /// </summary>
-            PValueScore,
-            /// <summary>
-            /// (MartingaleScore) The martingale score is thresholded.
-            /// </summary>
-            MartingaleScore
-        }
+        Positive,
+        /// <summary>
+        /// (Negative) Only negative anomalies are detected.
+        /// </summary>
+        Negative,
+        /// <summary>
+        /// (TwoSided) Both positive and negative anomalies are detected.
+        /// </summary>
+        TwoSided
+    }
 
+    /// <summary>
+    /// The score that should be thresholded to generate alerts.
+    /// </summary>
+    internal enum AlertingScore : byte
+    {
         /// <summary>
-        /// The base class that can be inherited by the 'Argument' classes in the derived classes containing the shared input parameters.
+        /// (RawScore) The raw anomaly score is thresholded.
         /// </summary>
-        public abstract class ArgumentsBase
-        {
-            [Argument(ArgumentType.Required, HelpText = "The name of the source column", ShortName = "src",
-                SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
-            public string Source;
+        RawScore,
+        /// <summary>
+        /// (PValueScore) The p-value score is thresholded.
+        /// </summary>
+        PValueScore,
+        /// <summary>
+        /// (MartingaleScore) The martingale score is thresholded.
+        /// </summary>
+        MartingaleScore
+    }
 
-            [Argument(ArgumentType.Required, HelpText = "The name of the new column", ShortName = "name",
-                SortOrder = 2)]
-            public string Name;
+    /// <summary>
+    /// The base class that can be inherited by the 'Argument' classes in the derived classes containing the shared input parameters.
+    /// </summary>
+    internal abstract class ArgumentsBase
+    {
+        [Argument(ArgumentType.Required, HelpText = "The name of the source column", ShortName = "src",
+            SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
+        public string Source;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The argument that determines whether to detect positive or negative anomalies, or both", ShortName = "side",
-                SortOrder = 3)]
-            public AnomalySide Side = AnomalySide.TwoSided;
+        [Argument(ArgumentType.Required, HelpText = "The name of the new column", ShortName = "name",
+            SortOrder = 2)]
+        public string Name;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The size of the sliding window for computing the p-value.", ShortName = "wnd",
-                SortOrder = 4)]
-            public int WindowSize = 1;
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The argument that determines whether to detect positive or negative anomalies, or both", ShortName = "side",
+            SortOrder = 3)]
+        public AnomalySide Side = AnomalySide.TwoSided;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The size of the initial window for computing the p-value as well as training if needed. The default value is set to 0, which means there is no initial window considered.",
-                ShortName = "initwnd", SortOrder = 5)]
-            public int InitialWindowSize = 0;
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The size of the sliding window for computing the p-value.", ShortName = "wnd",
+            SortOrder = 4)]
+        public int WindowSize = 1;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The martingale used for scoring",
-                ShortName = "martingale", SortOrder = 6)]
-            public MartingaleType Martingale = MartingaleType.Power;
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The size of the initial window for computing the p-value as well as training if needed. The default value is set to 0, which means there is no initial window considered.",
+            ShortName = "initwnd", SortOrder = 5)]
+        public int InitialWindowSize = 0;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The argument that determines whether anomalies should be detected based on the raw anomaly score, the p-value or the martingale score",
-                ShortName = "alert", SortOrder = 7)]
-            public AlertingScore AlertOn = AlertingScore.MartingaleScore;
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The martingale used for scoring",
+            ShortName = "martingale", SortOrder = 6)]
+        public MartingaleType Martingale = MartingaleType.Power;
 
-            [Argument(ArgumentType.AtMostOnce, HelpText = "The epsilon parameter for the Power martingale",
-                ShortName = "eps", SortOrder = 8)]
-            public Double PowerMartingaleEpsilon = 0.1;
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The argument that determines whether anomalies should be detected based on the raw anomaly score, the p-value or the martingale score",
+            ShortName = "alert", SortOrder = 7)]
+        public AlertingScore AlertOn = AlertingScore.MartingaleScore;
 
-            [Argument(ArgumentType.Required, HelpText = "The threshold for alerting",
-                ShortName = "thr", SortOrder = 9)]
-            public Double AlertThreshold;
-        }
+        [Argument(ArgumentType.AtMostOnce, HelpText = "The epsilon parameter for the Power martingale",
+            ShortName = "eps", SortOrder = 8)]
+        public Double PowerMartingaleEpsilon = 0.1;
 
-        // Determines the side of anomaly detection for this transform.
-        protected AnomalySide Side;
+        [Argument(ArgumentType.Required, HelpText = "The threshold for alerting",
+            ShortName = "thr", SortOrder = 9)]
+        public Double AlertThreshold;
+    }
 
-        // Determines the type of martingale used by this transform.
-        protected MartingaleType Martingale;
+        // REVIEW: This base class and its children classes generate one output column of type VBuffer<Double> to output 3 different anomaly scores as well as
+        // the alert flag. Ideally these 4 output information should be put in four seaparate columns instead of one VBuffer<> column. However, this is not currently
+        // possible due to our design restriction. This must be fixed in the next version and will potentially affect the children classes.
+        /// <summary>
+        /// The base class for sequential anomaly detection transforms that supports the p-value as well as the martingales scores computation from the sequence of
+        /// raw anomaly scores whose calculation is specified by the children classes. This class also provides mechanism for the threshold-based alerting on
+        /// the raw anomaly score, the p-value score or the martingale score. Currently, this class supports Power and Mixture martingales.
+        /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf
+        /// </summary>
+        /// <typeparam name="TInput">The type of the input sequence</typeparam>
+        /// <typeparam name="TState">The type of the input sequence</typeparam>
+        internal abstract class SequentialAnomalyDetectionTransformBase<TInput, TState> : SequentialTransformerBase<TInput, VBuffer<Double>, TState>
+        where TState : SequentialAnomalyDetectionTransformBase<TInput, TState>.AnomalyDetectionStateBase, new()
+        {
+            // Determines the side of anomaly detection for this transform.
+            internal AnomalySide Side;
 
-        // The epsilon parameter used by the Power martingale.
-        protected Double PowerMartingaleEpsilon;
+            // Determines the type of martingale used by this transform.
+            internal MartingaleType Martingale;
 
-        // Determines the score that should be thresholded to generate alerts by this transform.
-        protected AlertingScore ThresholdScore;
+            // The epsilon parameter used by the Power martingale.
+            internal Double PowerMartingaleEpsilon;
 
-        // Determines the threshold for generating alerts.
-        protected Double AlertThreshold;
+            // Determines the score that should be thresholded to generate alerts by this transform.
+            internal AlertingScore ThresholdScore;
 
-        // The size of the VBuffer in the dst column.
-        private int _outputLength;
+            // Determines the threshold for generating alerts.
+            internal Double AlertThreshold;
 
-        private static int GetOutputLength(AlertingScore alertingScore, IHostEnvironment host)
-        {
-            switch (alertingScore)
+            // The size of the VBuffer in the dst column.
+            internal int OutputLength;
+
+            // The minimum value for p-values. The smaller p-values are ceiled to this value.
+            internal const Double MinPValue = 1e-8;
+
+            // The maximun value for p-values. The larger p-values are floored to this value.
+            internal const Double MaxPValue = 1 - MinPValue;
+
+            private static int GetOutputLength(AlertingScore alertingScore, IHostEnvironment host)
+            {
+                switch (alertingScore)
+                {
+                    case AlertingScore.RawScore:
+                        return 2;
+                    case AlertingScore.PValueScore:
+                        return 3;
+                    case AlertingScore.MartingaleScore:
+                        return 4;
+                    default:
+                        throw host.Except("The alerting score can be only (0) RawScore, (1) PValueScore or (2) MartingaleScore.");
+                }
+            }
+
+            private protected SequentialAnomalyDetectionTransformBase(int windowSize, int initialWindowSize, string inputColumnName, string outputColumnName, string name, IHostEnvironment env,
+                AnomalySide anomalySide, MartingaleType martingale, AlertingScore alertingScore, Double powerMartingaleEpsilon,
+                Double alertThreshold)
+                : base(Contracts.CheckRef(env, nameof(env)).Register(name), windowSize, initialWindowSize, outputColumnName, inputColumnName, new VectorType(NumberType.R8, GetOutputLength(alertingScore, env)))
             {
-                case AlertingScore.RawScore:
-                    return 2;
-                case AlertingScore.PValueScore:
-                    return 3;
-                case AlertingScore.MartingaleScore:
-                    return 4;
-                default:
-                    throw host.Except("The alerting score can be only (0) RawScore, (1) PValueScore or (2) MartingaleScore.");
+                Host.CheckUserArg(Enum.IsDefined(typeof(MartingaleType), martingale), nameof(ArgumentsBase.Martingale), "Value is undefined.");
+                Host.CheckUserArg(Enum.IsDefined(typeof(AnomalySide), anomalySide), nameof(ArgumentsBase.Side), "Value is undefined.");
+                Host.CheckUserArg(Enum.IsDefined(typeof(AlertingScore), alertingScore), nameof(ArgumentsBase.AlertOn), "Value is undefined.");
+                Host.CheckUserArg(martingale != MartingaleType.None || alertingScore != AlertingScore.MartingaleScore, nameof(ArgumentsBase.Martingale), "A martingale type should be specified if alerting is based on the martingale score.");
+                Host.CheckUserArg(windowSize > 0 || alertingScore == AlertingScore.RawScore, nameof(ArgumentsBase.AlertOn),
+                    "When there is no windowed buffering (i.e., " + nameof(ArgumentsBase.WindowSize) + " = 0), the alert can be generated only based on the raw score (i.e., "
+                    + nameof(ArgumentsBase.AlertOn) + " = " + nameof(AlertingScore.RawScore) + ")");
+                Host.CheckUserArg(0 < powerMartingaleEpsilon && powerMartingaleEpsilon < 1, nameof(ArgumentsBase.PowerMartingaleEpsilon), "Should be in (0,1).");
+                Host.CheckUserArg(alertThreshold >= 0, nameof(ArgumentsBase.AlertThreshold), "Must be non-negative.");
+                Host.CheckUserArg(alertingScore != AlertingScore.PValueScore || (0 <= alertThreshold && alertThreshold <= 1), nameof(ArgumentsBase.AlertThreshold), "Must be in [0,1].");
+
+                ThresholdScore = alertingScore;
+                Side = anomalySide;
+                Martingale = martingale;
+                PowerMartingaleEpsilon = powerMartingaleEpsilon;
+                AlertThreshold = alertThreshold;
+                OutputLength = GetOutputLength(ThresholdScore, Host);
             }
-        }
 
-        private protected SequentialAnomalyDetectionTransformBase(int windowSize, int initialWindowSize, string inputColumnName, string outputColumnName, string name, IHostEnvironment env,
-            AnomalySide anomalySide, MartingaleType martingale, AlertingScore alertingScore, Double powerMartingaleEpsilon,
-            Double alertThreshold)
-            : base(Contracts.CheckRef(env, nameof(env)).Register(name), windowSize, initialWindowSize, outputColumnName, inputColumnName, new VectorType(NumberType.R8, GetOutputLength(alertingScore, env)))
-        {
-            Host.CheckUserArg(Enum.IsDefined(typeof(MartingaleType), martingale), nameof(ArgumentsBase.Martingale), "Value is undefined.");
-            Host.CheckUserArg(Enum.IsDefined(typeof(AnomalySide), anomalySide), nameof(ArgumentsBase.Side), "Value is undefined.");
-            Host.CheckUserArg(Enum.IsDefined(typeof(AlertingScore), alertingScore), nameof(ArgumentsBase.AlertOn), "Value is undefined.");
-            Host.CheckUserArg(martingale != MartingaleType.None || alertingScore != AlertingScore.MartingaleScore, nameof(ArgumentsBase.Martingale), "A martingale type should be specified if alerting is based on the martingale score.");
-            Host.CheckUserArg(windowSize > 0 || alertingScore == AlertingScore.RawScore, nameof(ArgumentsBase.AlertOn),
-                "When there is no windowed buffering (i.e., " + nameof(ArgumentsBase.WindowSize) + " = 0), the alert can be generated only based on the raw score (i.e., "
-                + nameof(ArgumentsBase.AlertOn) + " = " + nameof(AlertingScore.RawScore) + ")");
-            Host.CheckUserArg(0 < powerMartingaleEpsilon && powerMartingaleEpsilon < 1, nameof(ArgumentsBase.PowerMartingaleEpsilon), "Should be in (0,1).");
-            Host.CheckUserArg(alertThreshold >= 0, nameof(ArgumentsBase.AlertThreshold), "Must be non-negative.");
-            Host.CheckUserArg(alertingScore != AlertingScore.PValueScore || (0 <= alertThreshold && alertThreshold <= 1), nameof(ArgumentsBase.AlertThreshold), "Must be in [0,1].");
-
-            ThresholdScore = alertingScore;
-            Side = anomalySide;
-            Martingale = martingale;
-            PowerMartingaleEpsilon = powerMartingaleEpsilon;
-            AlertThreshold = alertThreshold;
-            _outputLength = GetOutputLength(ThresholdScore, Host);
-        }
+            private protected SequentialAnomalyDetectionTransformBase(ArgumentsBase args, string name, IHostEnvironment env)
+                : this(args.WindowSize, args.InitialWindowSize, args.Source, args.Name, name, env, args.Side, args.Martingale,
+                    args.AlertOn, args.PowerMartingaleEpsilon, args.AlertThreshold)
+            {
+            }
 
-        private protected SequentialAnomalyDetectionTransformBase(ArgumentsBase args, string name, IHostEnvironment env)
-            : this(args.WindowSize, args.InitialWindowSize, args.Source, args.Name, name, env, args.Side, args.Martingale,
-                args.AlertOn, args.PowerMartingaleEpsilon, args.AlertThreshold)
-        {
-        }
+            private protected SequentialAnomalyDetectionTransformBase(IHostEnvironment env, ModelLoadContext ctx, string name)
+                : base(Contracts.CheckRef(env, nameof(env)).Register(name), ctx)
+            {
+                // *** Binary format ***
+                // <base>
+                // byte: _martingale
+                // byte: _alertingScore
+                // byte: _anomalySide
+                // Double: _powerMartingaleEpsilon
+                // Double: _alertThreshold
+
+                byte temp;
+                temp = ctx.Reader.ReadByte();
+                Host.CheckDecode(Enum.IsDefined(typeof(MartingaleType), temp));
+                Martingale = (MartingaleType)temp;
+
+                temp = ctx.Reader.ReadByte();
+                Host.CheckDecode(Enum.IsDefined(typeof(AlertingScore), temp));
+                ThresholdScore = (AlertingScore)temp;
+
+                Host.CheckDecode(Martingale != MartingaleType.None || ThresholdScore != AlertingScore.MartingaleScore);
+                Host.CheckDecode(WindowSize > 0 || ThresholdScore == AlertingScore.RawScore);
+
+                temp = ctx.Reader.ReadByte();
+                Host.CheckDecode(Enum.IsDefined(typeof(AnomalySide), temp));
+                Side = (AnomalySide)temp;
+
+                PowerMartingaleEpsilon = ctx.Reader.ReadDouble();
+                Host.CheckDecode(0 < PowerMartingaleEpsilon && PowerMartingaleEpsilon < 1);
+
+                AlertThreshold = ctx.Reader.ReadDouble();
+                Host.CheckDecode(AlertThreshold >= 0);
+                Host.CheckDecode(ThresholdScore != AlertingScore.PValueScore || (0 <= AlertThreshold && AlertThreshold <= 1));
+
+                OutputLength = GetOutputLength(ThresholdScore, Host);
+            }
 
-        private protected SequentialAnomalyDetectionTransformBase(IHostEnvironment env, ModelLoadContext ctx, string name)
-            : base(Contracts.CheckRef(env, nameof(env)).Register(name), ctx)
-        {
-            // *** Binary format ***
-            // <base>
-            // byte: _martingale
-            // byte: _alertingScore
-            // byte: _anomalySide
-            // Double: _powerMartingaleEpsilon
-            // Double: _alertThreshold
-
-            byte temp;
-            temp = ctx.Reader.ReadByte();
-            Host.CheckDecode(Enum.IsDefined(typeof(MartingaleType), temp));
-            Martingale = (MartingaleType)temp;
-
-            temp = ctx.Reader.ReadByte();
-            Host.CheckDecode(Enum.IsDefined(typeof(AlertingScore), temp));
-            ThresholdScore = (AlertingScore)temp;
-
-            Host.CheckDecode(Martingale != MartingaleType.None || ThresholdScore != AlertingScore.MartingaleScore);
-            Host.CheckDecode(WindowSize > 0 || ThresholdScore == AlertingScore.RawScore);
-
-            temp = ctx.Reader.ReadByte();
-            Host.CheckDecode(Enum.IsDefined(typeof(AnomalySide), temp));
-            Side = (AnomalySide)temp;
-
-            PowerMartingaleEpsilon = ctx.Reader.ReadDouble();
-            Host.CheckDecode(0 < PowerMartingaleEpsilon && PowerMartingaleEpsilon < 1);
-
-            AlertThreshold = ctx.Reader.ReadDouble();
-            Host.CheckDecode(AlertThreshold >= 0);
-            Host.CheckDecode(ThresholdScore != AlertingScore.PValueScore || (0 <= AlertThreshold && AlertThreshold <= 1));
-
-            _outputLength = GetOutputLength(ThresholdScore, Host);
-        }
+            private protected override void SaveModel(ModelSaveContext ctx)
+            {
+                Host.CheckValue(ctx, nameof(ctx));
+                ctx.CheckAtModel();
+
+                Host.Assert(Enum.IsDefined(typeof(MartingaleType), Martingale));
+                Host.Assert(Enum.IsDefined(typeof(AlertingScore), ThresholdScore));
+                Host.Assert(Martingale != MartingaleType.None || ThresholdScore != AlertingScore.MartingaleScore);
+                Host.Assert(WindowSize > 0 || ThresholdScore == AlertingScore.RawScore);
+                Host.Assert(Enum.IsDefined(typeof(AnomalySide), Side));
+                Host.Assert(0 < PowerMartingaleEpsilon && PowerMartingaleEpsilon < 1);
+                Host.Assert(AlertThreshold >= 0);
+                Host.Assert(ThresholdScore != AlertingScore.PValueScore || (0 <= AlertThreshold && AlertThreshold <= 1));
+
+                // *** Binary format ***
+                // <base>
+                // byte: _martingale
+                // byte: _alertingScore
+                // byte: _anomalySide
+                // Double: _powerMartingaleEpsilon
+                // Double: _alertThreshold
+
+                base.SaveModel(ctx);
+                ctx.Writer.Write((byte)Martingale);
+                ctx.Writer.Write((byte)ThresholdScore);
+                ctx.Writer.Write((byte)Side);
+                ctx.Writer.Write(PowerMartingaleEpsilon);
+                ctx.Writer.Write(AlertThreshold);
+            }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
-        {
-            Host.CheckValue(ctx, nameof(ctx));
-            ctx.CheckAtModel();
-
-            Host.Assert(Enum.IsDefined(typeof(MartingaleType), Martingale));
-            Host.Assert(Enum.IsDefined(typeof(AlertingScore), ThresholdScore));
-            Host.Assert(Martingale != MartingaleType.None || ThresholdScore != AlertingScore.MartingaleScore);
-            Host.Assert(WindowSize > 0 || ThresholdScore == AlertingScore.RawScore);
-            Host.Assert(Enum.IsDefined(typeof(AnomalySide), Side));
-            Host.Assert(0 < PowerMartingaleEpsilon && PowerMartingaleEpsilon < 1);
-            Host.Assert(AlertThreshold >= 0);
-            Host.Assert(ThresholdScore != AlertingScore.PValueScore || (0 <= AlertThreshold && AlertThreshold <= 1));
-
-            // *** Binary format ***
-            // <base>
-            // byte: _martingale
-            // byte: _alertingScore
-            // byte: _anomalySide
-            // Double: _powerMartingaleEpsilon
-            // Double: _alertThreshold
-
-            base.SaveModel(ctx);
-            ctx.Writer.Write((byte)Martingale);
-            ctx.Writer.Write((byte)ThresholdScore);
-            ctx.Writer.Write((byte)Side);
-            ctx.Writer.Write(PowerMartingaleEpsilon);
-            ctx.Writer.Write(AlertThreshold);
-        }
+            /// <summary>
+            /// Calculates the betting function for the Power martingale in the log scale.
+            /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf.
+            /// </summary>
+            /// <param name="p">The p-value</param>
+            /// <param name="epsilon">The epsilon</param>
+            /// <returns>The Power martingale betting function value in the natural logarithmic scale.</returns>
+            internal Double LogPowerMartigaleBettingFunc(Double p, Double epsilon)
+            {
+                Host.Assert(MinPValue > 0);
+                Host.Assert(MaxPValue < 1);
+                Host.Assert(MinPValue <= p && p <= MaxPValue);
+                Host.Assert(0 < epsilon && epsilon < 1);
 
-        // The minimum value for p-values. The smaller p-values are ceiled to this value.
-        private const Double MinPValue = 1e-8;
+                return Math.Log(epsilon) + (epsilon - 1) * Math.Log(p);
+            }
 
-        // The maximun value for p-values. The larger p-values are floored to this value.
-        private const Double MaxPValue = 1 - MinPValue;
+            /// <summary>
+            /// Calculates the betting function for the Mixture martingale in the log scale.
+            /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf.
+            /// </summary>
+            /// <param name="p">The p-value</param>
+            /// <returns>The Mixure (marginalized over epsilon) martingale betting function value in the natural logarithmic scale.</returns>
+            internal Double LogMixtureMartigaleBettingFunc(Double p)
+            {
+                Host.Assert(MinPValue > 0);
+                Host.Assert(MaxPValue < 1);
+                Host.Assert(MinPValue <= p && p <= MaxPValue);
 
-        /// <summary>
-        /// Calculates the betting function for the Power martingale in the log scale.
-        /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf.
-        /// </summary>
-        /// <param name="p">The p-value</param>
-        /// <param name="epsilon">The epsilon</param>
-        /// <returns>The Power martingale betting function value in the natural logarithmic scale.</returns>
-        protected Double LogPowerMartigaleBettingFunc(Double p, Double epsilon)
-        {
-            Host.Assert(MinPValue > 0);
-            Host.Assert(MaxPValue < 1);
-            Host.Assert(MinPValue <= p && p <= MaxPValue);
-            Host.Assert(0 < epsilon && epsilon < 1);
+                Double logP = Math.Log(p);
+                return Math.Log(p * logP + 1 - p) - 2 * Math.Log(-logP) - logP;
+            }
 
-            return Math.Log(epsilon) + (epsilon - 1) * Math.Log(p);
-        }
+            internal override IStatefulRowMapper MakeRowMapper(Schema schema) => new Mapper(Host, this, schema);
 
-        /// <summary>
-        /// Calculates the betting function for the Mixture martingale in the log scale.
-        /// For more details, please refer to http://arxiv.org/pdf/1204.3251.pdf.
-        /// </summary>
-        /// <param name="p">The p-value</param>
-        /// <returns>The Mixure (marginalized over epsilon) martingale betting function value in the natural logarithmic scale.</returns>
-        protected Double LogMixtureMartigaleBettingFunc(Double p)
-        {
-            Host.Assert(MinPValue > 0);
-            Host.Assert(MaxPValue < 1);
-            Host.Assert(MinPValue <= p && p <= MaxPValue);
+            internal sealed class Mapper : IStatefulRowMapper
+            {
+                private readonly IHost _host;
+                private readonly SequentialAnomalyDetectionTransformBase<TInput, TState> _parent;
+                private readonly Schema _parentSchema;
+                private readonly int _inputColumnIndex;
+                private readonly VBuffer<ReadOnlyMemory<Char>> _slotNames;
+                private AnomalyDetectionStateBase State { get; set; }
+
+                public Mapper(IHostEnvironment env, SequentialAnomalyDetectionTransformBase<TInput, TState> parent, Schema inputSchema)
+                {
+                    Contracts.CheckValue(env, nameof(env));
+                    _host = env.Register(nameof(Mapper));
+                    _host.CheckValue(inputSchema, nameof(inputSchema));
+                    _host.CheckValue(parent, nameof(parent));
 
-            Double logP = Math.Log(p);
-            return Math.Log(p * logP + 1 - p) - 2 * Math.Log(-logP) - logP;
-        }
+                    if (!inputSchema.TryGetColumnIndex(parent.InputColumnName, out _inputColumnIndex))
+                        throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", parent.InputColumnName);
 
-        /// <summary>
-        /// The base state class for sequential anomaly detection: this class implements the p-values and martinagle calculations for anomaly detection
-        /// given that the raw anomaly score calculation is specified by the derived classes.
-        /// </summary>
-        public abstract class AnomalyDetectionStateBase : StateBase
-        {
-            // A reference to the parent transform.
-            protected SequentialAnomalyDetectionTransformBase<TInput, TState> Parent;
+                    var colType = inputSchema[_inputColumnIndex].Type;
+                    if (colType != NumberType.R4)
+                        throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", parent.InputColumnName, "float", colType.ToString());
 
-            // A windowed buffer to cache the update values to the martingale score in the log scale.
-            private FixedSizeQueue<Double> LogMartingaleUpdateBuffer { get; set; }
+                    _parent = parent;
+                    _parentSchema = inputSchema;
+                    _slotNames = new VBuffer<ReadOnlyMemory<char>>(4, new[] { "Alert".AsMemory(), "Raw Score".AsMemory(),
+                    "P-Value Score".AsMemory(), "Martingale Score".AsMemory() });
 
-            // A windowed buffer to cache the raw anomaly scores for p-value calculation.
-            private FixedSizeQueue<Single> RawScoreBuffer { get; set; }
+                    State = (AnomalyDetectionStateBase)_parent.StateRef;
+                }
 
-            // The current martingale score in the log scale.
-            private Double _logMartingaleValue;
+                public Schema.DetachedColumn[] GetOutputColumns()
+                {
+                    var meta = new MetadataBuilder();
+                    meta.AddSlotNames(_parent.OutputLength, GetSlotNames);
+                    var info = new Schema.DetachedColumn[1];
+                    info[0] = new Schema.DetachedColumn(_parent.OutputColumnName, new VectorType(NumberType.R8, _parent.OutputLength), meta.GetMetadata());
+                    return info;
+                }
 
-            // Sum of the squared Euclidean distances among the raw socres in the buffer.
-            // Used for computing the optimal bandwidth for Kernel Density Estimation of p-values.
-            private Double _sumSquaredDist;
+                public void GetSlotNames(ref VBuffer<ReadOnlyMemory<char>> dst) => _slotNames.CopyTo(ref dst, 0, _parent.OutputLength);
 
-            private int _martingaleAlertCounter;
+                public Func<int, bool> GetDependencies(Func<int, bool> activeOutput)
+                {
+                    if (activeOutput(0))
+                        return col => col == _inputColumnIndex;
+                    else
+                        return col => false;
+                }
 
-            protected Double LatestMartingaleScore => Math.Exp(_logMartingaleValue);
+                public void Save(ModelSaveContext ctx) => _parent.SaveModel(ctx);
 
-            private protected AnomalyDetectionStateBase() { }
+                public Delegate[] CreateGetters(Row input, Func<int, bool> activeOutput, out Action disposer)
+                {
+                    disposer = null;
+                    var getters = new Delegate[1];
+                    if (activeOutput(0))
+                        getters[0] = MakeGetter(input, State);
 
-            private protected override void CloneCore(StateBase state)
-            {
-                base.CloneCore(state);
-                Contracts.Assert(state is AnomalyDetectionStateBase);
-                var stateLocal = state as AnomalyDetectionStateBase;
-                stateLocal.LogMartingaleUpdateBuffer = LogMartingaleUpdateBuffer.Clone();
-                stateLocal.RawScoreBuffer = RawScoreBuffer.Clone();
-            }
+                    return getters;
+                }
 
-            private protected AnomalyDetectionStateBase(BinaryReader reader) : base(reader)
-            {
-                LogMartingaleUpdateBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueDouble(reader, Host);
-                RawScoreBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
-                _logMartingaleValue = reader.ReadDouble();
-                _sumSquaredDist = reader.ReadDouble();
-                _martingaleAlertCounter = reader.ReadInt32();
-            }
+                private delegate void ProcessData(ref TInput src, ref VBuffer<double> dst);
 
-            internal override void Save(BinaryWriter writer)
-            {
-                base.Save(writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(LogMartingaleUpdateBuffer, writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(RawScoreBuffer, writer);
-                writer.Write(_logMartingaleValue);
-                writer.Write(_sumSquaredDist);
-                writer.Write(_martingaleAlertCounter);
-            }
+                private Delegate MakeGetter(Row input, AnomalyDetectionStateBase state)
+                {
+                    _host.AssertValue(input);
+                    var srcGetter = input.GetGetter<TInput>(_inputColumnIndex);
+                    ProcessData processData = _parent.WindowSize > 0 ?
+                        (ProcessData)state.Process : state.ProcessWithoutBuffer;
 
-            private Double ComputeKernelPValue(Double rawScore)
-            {
-                int i;
-                int n = RawScoreBuffer.Count;
+                    ValueGetter<VBuffer<double>> valueGetter = (ref VBuffer<double> dst) =>
+                    {
+                        TInput src = default;
+                        srcGetter(ref src);
+                        processData(ref src, ref dst);
+                    };
+                    return valueGetter;
+                }
 
-                if (n == 0)
-                    return 0.5;
+                public Action<long> CreatePinger(Row input, Func<int, bool> activeOutput, out Action disposer)
+                {
+                    disposer = null;
+                    Action<long> pinger = null;
+                    if (activeOutput(0))
+                        pinger = MakePinger(input, State);
 
-                Double pValue = 0;
-                Double bandWidth = Math.Sqrt(2) * ((n == 1) ? 1 : Math.Sqrt(_sumSquaredDist) / n);
-                bandWidth = Math.Max(bandWidth, 1e-6);
+                    return pinger;
+                }
 
-                Double diff;
-                for (i = 0; i < n; ++i)
+                private Action<long> MakePinger(Row input, AnomalyDetectionStateBase state)
                 {
-                    diff = rawScore - RawScoreBuffer[i];
-                    pValue -= ProbabilityFunctions.Erf(diff / bandWidth);
-                    _sumSquaredDist += diff * diff;
+                    _host.AssertValue(input);
+                    var srcGetter = input.GetGetter<TInput>(_inputColumnIndex);
+                    Action<long> pinger = (long rowPosition) =>
+                    {
+                        TInput src = default;
+                        srcGetter(ref src);
+                        state.UpdateState(ref src, rowPosition, _parent.WindowSize > 0);
+                    };
+                    return pinger;
                 }
 
-                pValue = 0.5 + pValue / (2 * n);
-                if (RawScoreBuffer.IsFull)
+                public void CloneState()
                 {
-                    for (i = 1; i < n; ++i)
+                    if (Interlocked.Increment(ref _parent.StateRefCount) > 1)
                     {
-                        diff = RawScoreBuffer[0] - RawScoreBuffer[i];
-                        _sumSquaredDist -= diff * diff;
+                        State = (AnomalyDetectionStateBase)_parent.StateRef.Clone();
                     }
-
-                    diff = RawScoreBuffer[0] - rawScore;
-                    _sumSquaredDist -= diff * diff;
                 }
 
-                return pValue;
+                public ITransformer GetTransformer()
+                {
+                    return _parent;
+                }
             }
-
-            private protected override void SetNaOutput(ref VBuffer<Double> dst)
+            /// <summary>
+            /// The base state class for sequential anomaly detection: this class implements the p-values and martinagle calculations for anomaly detection
+            /// given that the raw anomaly score calculation is specified by the derived classes.
+            /// </summary>
+            internal abstract class AnomalyDetectionStateBase : SequentialTransformerBase<TInput, VBuffer<Double>, TState>.StateBase
             {
-                var outputLength = Parent._outputLength;
-                var editor = VBufferEditor.Create(ref dst, outputLength);
+                // A reference to the parent transform.
+                protected SequentialAnomalyDetectionTransformBase<TInput, TState> Parent;
 
-                for (int i = 0; i < outputLength; ++i)
-                    editor.Values[i] = Double.NaN;
-
-                dst = editor.Commit();
-            }
+                // A windowed buffer to cache the update values to the martingale score in the log scale.
+                private FixedSizeQueue<Double> LogMartingaleUpdateBuffer { get; set; }
 
-            private protected override sealed void TransformCore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration, ref VBuffer<Double> dst)
-            {
-                var outputLength = Parent._outputLength;
-                Host.Assert(outputLength >= 2);
+                // A windowed buffer to cache the raw anomaly scores for p-value calculation.
+                private FixedSizeQueue<Single> RawScoreBuffer { get; set; }
 
-                var result = VBufferEditor.Create(ref dst, outputLength);
-                float rawScore = 0;
+                // The current martingale score in the log scale.
+                private Double _logMartingaleValue;
 
-                for (int i = 0; i < outputLength; ++i)
-                    result.Values[i] = Double.NaN;
+                // Sum of the squared Euclidean distances among the raw socres in the buffer.
+                // Used for computing the optimal bandwidth for Kernel Density Estimation of p-values.
+                private Double _sumSquaredDist;
 
-                // Step 1: Computing the raw anomaly score
-                result.Values[1] = ComputeRawAnomalyScore(ref input, windowedBuffer, iteration);
+                private int _martingaleAlertCounter;
 
-                if (Double.IsNaN(result.Values[1]))
-                    result.Values[0] = 0;
-                else
-                {
-                    if (WindowSize > 0)
-                    {
-                        // Step 2: Computing the p-value score
-                        rawScore = (float)result.Values[1];
-                        if (Parent.ThresholdScore == AlertingScore.RawScore)
-                        {
-                            switch (Parent.Side)
-                            {
-                                case AnomalySide.Negative:
-                                    rawScore = (float)(-result.Values[1]);
-                                    break;
+                protected Double LatestMartingaleScore => Math.Exp(_logMartingaleValue);
 
-                                case AnomalySide.Positive:
-                                    break;
+                private protected AnomalyDetectionStateBase() { }
 
-                                default:
-                                    rawScore = (float)Math.Abs(result.Values[1]);
-                                    break;
-                            }
-                        }
-                        else
-                        {
-                            result.Values[2] = ComputeKernelPValue(rawScore);
-
-                            switch (Parent.Side)
-                            {
-                                case AnomalySide.Negative:
-                                    result.Values[2] = 1 - result.Values[2];
-                                    break;
-
-                                case AnomalySide.Positive:
-                                    break;
-
-                                default:
-                                    result.Values[2] = Math.Min(result.Values[2], 1 - result.Values[2]);
-                                    break;
-                            }
+                private protected override void CloneCore(TState state)
+                {
+                    base.CloneCore(state);
+                    Contracts.Assert(state is AnomalyDetectionStateBase);
+                    var stateLocal = state as AnomalyDetectionStateBase;
+                    stateLocal.LogMartingaleUpdateBuffer = LogMartingaleUpdateBuffer.Clone();
+                    stateLocal.RawScoreBuffer = RawScoreBuffer.Clone();
+                }
 
-                            // Keeping the p-value in the safe range
-                            if (result.Values[2] < MinPValue)
-                                result.Values[2] = MinPValue;
-                            else if (result.Values[2] > MaxPValue)
-                                result.Values[2] = MaxPValue;
+                private protected AnomalyDetectionStateBase(BinaryReader reader) : base(reader)
+                {
+                    LogMartingaleUpdateBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueDouble(reader, Host);
+                    RawScoreBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                    _logMartingaleValue = reader.ReadDouble();
+                    _sumSquaredDist = reader.ReadDouble();
+                    _martingaleAlertCounter = reader.ReadInt32();
+                }
 
-                            RawScoreBuffer.AddLast(rawScore);
+                internal override void Save(BinaryWriter writer)
+                {
+                    base.Save(writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(LogMartingaleUpdateBuffer, writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(RawScoreBuffer, writer);
+                    writer.Write(_logMartingaleValue);
+                    writer.Write(_sumSquaredDist);
+                    writer.Write(_martingaleAlertCounter);
+                }
 
-                            // Step 3: Computing the martingale value
-                            if (Parent.Martingale != MartingaleType.None && Parent.ThresholdScore == AlertingScore.MartingaleScore)
-                            {
-                                Double martingaleUpdate = 0;
-                                switch (Parent.Martingale)
-                                {
-                                    case MartingaleType.Power:
-                                        martingaleUpdate = Parent.LogPowerMartigaleBettingFunc(result.Values[2], Parent.PowerMartingaleEpsilon);
-                                        break;
+                private Double ComputeKernelPValue(Double rawScore)
+                {
+                    int i;
+                    int n = RawScoreBuffer.Count;
 
-                                    case MartingaleType.Mixture:
-                                        martingaleUpdate = Parent.LogMixtureMartigaleBettingFunc(result.Values[2]);
-                                        break;
-                                }
+                    if (n == 0)
+                        return 0.5;
 
-                                if (LogMartingaleUpdateBuffer.Count == 0)
-                                {
-                                    for (int i = 0; i < LogMartingaleUpdateBuffer.Capacity; ++i)
-                                        LogMartingaleUpdateBuffer.AddLast(martingaleUpdate);
-                                    _logMartingaleValue += LogMartingaleUpdateBuffer.Capacity * martingaleUpdate;
-                                }
-                                else
-                                {
-                                    _logMartingaleValue += martingaleUpdate;
-                                    _logMartingaleValue -= LogMartingaleUpdateBuffer.PeekFirst();
-                                    LogMartingaleUpdateBuffer.AddLast(martingaleUpdate);
-                                }
+                    Double pValue = 0;
+                    Double bandWidth = Math.Sqrt(2) * ((n == 1) ? 1 : Math.Sqrt(_sumSquaredDist) / n);
+                    bandWidth = Math.Max(bandWidth, 1e-6);
 
-                                result.Values[3] = Math.Exp(_logMartingaleValue);
-                            }
-                        }
+                    Double diff;
+                    for (i = 0; i < n; ++i)
+                    {
+                        diff = rawScore - RawScoreBuffer[i];
+                        pValue -= ProbabilityFunctions.Erf(diff / bandWidth);
+                        _sumSquaredDist += diff * diff;
                     }
 
-                    // Generating alert
-                    bool alert = false;
-
-                    if (RawScoreBuffer.IsFull) // No alert until the buffer is completely full.
+                    pValue = 0.5 + pValue / (2 * n);
+                    if (RawScoreBuffer.IsFull)
                     {
-                        switch (Parent.ThresholdScore)
+                        for (i = 1; i < n; ++i)
                         {
-                            case AlertingScore.RawScore:
-                                alert = rawScore >= Parent.AlertThreshold;
-                                break;
-                            case AlertingScore.PValueScore:
-                                alert = result.Values[2] <= Parent.AlertThreshold;
-                                break;
-                            case AlertingScore.MartingaleScore:
-                                alert = (Parent.Martingale != MartingaleType.None) && (result.Values[3] >= Parent.AlertThreshold);
-
-                                if (alert)
-                                {
-                                    if (_martingaleAlertCounter > 0)
-                                        alert = false;
-                                    else
-                                        _martingaleAlertCounter = Parent.WindowSize;
-                                }
-
-                                _martingaleAlertCounter--;
-                                _martingaleAlertCounter = _martingaleAlertCounter < 0 ? 0 : _martingaleAlertCounter;
-                                break;
+                            diff = RawScoreBuffer[0] - RawScoreBuffer[i];
+                            _sumSquaredDist -= diff * diff;
                         }
+
+                        diff = RawScoreBuffer[0] - rawScore;
+                        _sumSquaredDist -= diff * diff;
                     }
 
-                    result.Values[0] = Convert.ToDouble(alert);
+                    return pValue;
                 }
 
-                dst = result.Commit();
-            }
-
-            private protected override sealed void InitializeStateCore(bool disk = false)
-            {
-                Parent = (SequentialAnomalyDetectionTransformBase<TInput, TState>)ParentTransform;
-                Host.Assert(WindowSize >= 0);
-
-                if (disk == false)
+                private protected override void SetNaOutput(ref VBuffer<Double> dst)
                 {
-                    if (Parent.Martingale != MartingaleType.None)
-                        LogMartingaleUpdateBuffer = new FixedSizeQueue<Double>(WindowSize == 0 ? 1 : WindowSize);
-                    else
-                        LogMartingaleUpdateBuffer = new FixedSizeQueue<Double>(1);
+                    var outputLength = Parent.OutputLength;
+                    var editor = VBufferEditor.Create(ref dst, outputLength);
 
-                    RawScoreBuffer = new FixedSizeQueue<float>(WindowSize == 0 ? 1 : WindowSize);
+                    for (int i = 0; i < outputLength; ++i)
+                        editor.Values[i] = Double.NaN;
 
-                    _logMartingaleValue = 0;
+                    dst = editor.Commit();
                 }
 
-                InitializeAnomalyDetector();
-            }
+                private protected override sealed void TransformCore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration, ref VBuffer<Double> dst)
+                {
+                    var outputLength = Parent.OutputLength;
+                    Host.Assert(outputLength >= 2);
 
-            /// <summary>
-            /// The abstract method that realizes the initialization functionality for the anomaly detector.
-            /// </summary>
-            private protected abstract void InitializeAnomalyDetector();
+                    var result = VBufferEditor.Create(ref dst, outputLength);
+                    float rawScore = 0;
 
-            /// <summary>
-            /// The abstract method that realizes the main logic for calculating the raw anomaly score bfor the current input given a windowed buffer
-            /// </summary>
-            /// <param name="input">A reference to the input object.</param>
-            /// <param name="windowedBuffer">A reference to the windowed buffer.</param>
-            /// <param name="iteration">A long number that indicates the number of times ComputeRawAnomalyScore has been called so far (starting value = 0).</param>
-            /// <returns>The raw anomaly score for the input. The Assumption is the higher absolute value of the raw score, the more anomalous the input is.
-            /// The sign of the score determines whether it's a positive anomaly or a negative one.</returns>
-            private protected abstract Double ComputeRawAnomalyScore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration);
-        }
+                    for (int i = 0; i < outputLength; ++i)
+                        result.Values[i] = Double.NaN;
 
-        private protected override IStatefulRowMapper MakeRowMapper(Schema schema) => new Mapper(Host, this, schema);
+                    // Step 1: Computing the raw anomaly score
+                    result.Values[1] = ComputeRawAnomalyScore(ref input, windowedBuffer, iteration);
 
-        private sealed class Mapper : IStatefulRowMapper
-        {
-            private readonly IHost _host;
-            private readonly SequentialAnomalyDetectionTransformBase<TInput, TState> _parent;
-            private readonly Schema _parentSchema;
-            private readonly int _inputColumnIndex;
-            private readonly VBuffer<ReadOnlyMemory<Char>> _slotNames;
-            private TState State { get; set; }
-
-            public Mapper(IHostEnvironment env, SequentialAnomalyDetectionTransformBase<TInput, TState> parent, Schema inputSchema)
-            {
-                Contracts.CheckValue(env, nameof(env));
-                _host = env.Register(nameof(Mapper));
-                _host.CheckValue(inputSchema, nameof(inputSchema));
-                _host.CheckValue(parent, nameof(parent));
+                    if (Double.IsNaN(result.Values[1]))
+                        result.Values[0] = 0;
+                    else
+                    {
+                        if (WindowSize > 0)
+                        {
+                            // Step 2: Computing the p-value score
+                            rawScore = (float)result.Values[1];
+                            if (Parent.ThresholdScore == AlertingScore.RawScore)
+                            {
+                                switch (Parent.Side)
+                                {
+                                    case AnomalySide.Negative:
+                                        rawScore = (float)(-result.Values[1]);
+                                        break;
 
-                if (!inputSchema.TryGetColumnIndex(parent.InputColumnName, out _inputColumnIndex))
-                    throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", parent.InputColumnName);
+                                    case AnomalySide.Positive:
+                                        break;
 
-                var colType = inputSchema[_inputColumnIndex].Type;
-                if (colType != NumberType.R4)
-                    throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", parent.InputColumnName, "float", colType.ToString());
+                                    default:
+                                        rawScore = (float)Math.Abs(result.Values[1]);
+                                        break;
+                                }
+                            }
+                            else
+                            {
+                                result.Values[2] = ComputeKernelPValue(rawScore);
 
-                _parent = parent;
-                _parentSchema = inputSchema;
-                _slotNames = new VBuffer<ReadOnlyMemory<char>>(4, new[] { "Alert".AsMemory(), "Raw Score".AsMemory(),
-                    "P-Value Score".AsMemory(), "Martingale Score".AsMemory() });
+                                switch (Parent.Side)
+                                {
+                                    case AnomalySide.Negative:
+                                        result.Values[2] = 1 - result.Values[2];
+                                        break;
 
-                State = _parent.StateRef;
-            }
+                                    case AnomalySide.Positive:
+                                        break;
 
-            public Schema.DetachedColumn[] GetOutputColumns()
-            {
-                var meta = new MetadataBuilder();
-                meta.AddSlotNames(_parent._outputLength, GetSlotNames);
-                var info = new Schema.DetachedColumn[1];
-                info[0] = new Schema.DetachedColumn(_parent.OutputColumnName, new VectorType(NumberType.R8, _parent._outputLength), meta.GetMetadata());
-                return info;
-            }
+                                    default:
+                                        result.Values[2] = Math.Min(result.Values[2], 1 - result.Values[2]);
+                                        break;
+                                }
 
-            public void GetSlotNames(ref VBuffer<ReadOnlyMemory<char>> dst) => _slotNames.CopyTo(ref dst, 0, _parent._outputLength);
+                                // Keeping the p-value in the safe range
+                                if (result.Values[2] < SequentialAnomalyDetectionTransformBase<TInput, TState>.MinPValue)
+                                    result.Values[2] = SequentialAnomalyDetectionTransformBase<TInput, TState>.MinPValue;
+                                else if (result.Values[2] > SequentialAnomalyDetectionTransformBase<TInput, TState>.MaxPValue)
+                                    result.Values[2] = SequentialAnomalyDetectionTransformBase<TInput, TState>.MaxPValue;
 
-            public Func<int, bool> GetDependencies(Func<int, bool> activeOutput)
-            {
-                if (activeOutput(0))
-                    return col => col == _inputColumnIndex;
-                else
-                    return col => false;
-            }
+                                RawScoreBuffer.AddLast(rawScore);
+
+                                // Step 3: Computing the martingale value
+                                if (Parent.Martingale != MartingaleType.None && Parent.ThresholdScore == AlertingScore.MartingaleScore)
+                                {
+                                    Double martingaleUpdate = 0;
+                                    switch (Parent.Martingale)
+                                    {
+                                        case MartingaleType.Power:
+                                            martingaleUpdate = Parent.LogPowerMartigaleBettingFunc(result.Values[2], Parent.PowerMartingaleEpsilon);
+                                            break;
+
+                                        case MartingaleType.Mixture:
+                                            martingaleUpdate = Parent.LogMixtureMartigaleBettingFunc(result.Values[2]);
+                                            break;
+                                    }
+
+                                    if (LogMartingaleUpdateBuffer.Count == 0)
+                                    {
+                                        for (int i = 0; i < LogMartingaleUpdateBuffer.Capacity; ++i)
+                                            LogMartingaleUpdateBuffer.AddLast(martingaleUpdate);
+                                        _logMartingaleValue += LogMartingaleUpdateBuffer.Capacity * martingaleUpdate;
+                                    }
+                                    else
+                                    {
+                                        _logMartingaleValue += martingaleUpdate;
+                                        _logMartingaleValue -= LogMartingaleUpdateBuffer.PeekFirst();
+                                        LogMartingaleUpdateBuffer.AddLast(martingaleUpdate);
+                                    }
 
-            void ICanSaveModel.Save(ModelSaveContext ctx) => _parent.SaveModel(ctx);
+                                    result.Values[3] = Math.Exp(_logMartingaleValue);
+                                }
+                            }
+                        }
 
-            public Delegate[] CreateGetters(Row input, Func<int, bool> activeOutput, out Action disposer)
-            {
-                disposer = null;
-                var getters = new Delegate[1];
-                if (activeOutput(0))
-                    getters[0] = MakeGetter(input, State);
+                        // Generating alert
+                        bool alert = false;
 
-                return getters;
-            }
+                        if (RawScoreBuffer.IsFull) // No alert until the buffer is completely full.
+                        {
+                            switch (Parent.ThresholdScore)
+                            {
+                                case AlertingScore.RawScore:
+                                    alert = rawScore >= Parent.AlertThreshold;
+                                    break;
+                                case AlertingScore.PValueScore:
+                                    alert = result.Values[2] <= Parent.AlertThreshold;
+                                    break;
+                                case AlertingScore.MartingaleScore:
+                                    alert = (Parent.Martingale != MartingaleType.None) && (result.Values[3] >= Parent.AlertThreshold);
+
+                                    if (alert)
+                                    {
+                                        if (_martingaleAlertCounter > 0)
+                                            alert = false;
+                                        else
+                                            _martingaleAlertCounter = Parent.WindowSize;
+                                    }
+
+                                    _martingaleAlertCounter--;
+                                    _martingaleAlertCounter = _martingaleAlertCounter < 0 ? 0 : _martingaleAlertCounter;
+                                    break;
+                            }
+                        }
 
-            private delegate void ProcessData(ref TInput src, ref VBuffer<double> dst);
+                        result.Values[0] = Convert.ToDouble(alert);
+                    }
 
-            private Delegate MakeGetter(Row input, TState state)
-            {
-                _host.AssertValue(input);
-                var srcGetter = input.GetGetter<TInput>(_inputColumnIndex);
-                ProcessData processData = _parent.WindowSize > 0 ?
-                    (ProcessData)state.Process : state.ProcessWithoutBuffer;
+                    dst = result.Commit();
+                }
 
-                ValueGetter<VBuffer<double>> valueGetter = (ref VBuffer<double> dst) =>
+                private protected override sealed void InitializeStateCore(bool disk = false)
                 {
-                   TInput src = default;
-                   srcGetter(ref src);
-                   processData(ref src, ref dst);
-                };
-                return valueGetter;
-            }
+                    Parent = (SequentialAnomalyDetectionTransformBase<TInput, TState>)ParentTransform;
+                    Host.Assert(WindowSize >= 0);
 
-            public Action<long> CreatePinger(Row input, Func<int, bool> activeOutput, out Action disposer)
-            {
-                disposer = null;
-                Action<long> pinger = null;
-                if (activeOutput(0))
-                    pinger = MakePinger(input, State);
+                    if (disk == false)
+                    {
+                        if (Parent.Martingale != MartingaleType.None)
+                            LogMartingaleUpdateBuffer = new FixedSizeQueue<Double>(WindowSize == 0 ? 1 : WindowSize);
+                        else
+                            LogMartingaleUpdateBuffer = new FixedSizeQueue<Double>(1);
 
-                return pinger;
-            }
+                        RawScoreBuffer = new FixedSizeQueue<float>(WindowSize == 0 ? 1 : WindowSize);
 
-            private Action<long> MakePinger(Row input, TState state)
-            {
-                _host.AssertValue(input);
-                var srcGetter = input.GetGetter<TInput>(_inputColumnIndex);
-                Action<long> pinger = (long rowPosition) =>
-                {
-                    TInput src = default;
-                    srcGetter(ref src);
-                    state.UpdateState(ref src, rowPosition, _parent.WindowSize > 0);
-                };
-                return pinger;
-            }
+                        _logMartingaleValue = 0;
+                    }
 
-            public void CloneState()
-            {
-                if (Interlocked.Increment(ref _parent.StateRefCount) > 1)
-                {
-                    State = (TState)_parent.StateRef.Clone();
+                    InitializeAnomalyDetector();
                 }
-            }
 
-            public ITransformer GetTransformer()
-            {
-                return _parent;
+                /// <summary>
+                /// The abstract method that realizes the initialization functionality for the anomaly detector.
+                /// </summary>
+                private protected abstract void InitializeAnomalyDetector();
+
+                /// <summary>
+                /// The abstract method that realizes the main logic for calculating the raw anomaly score bfor the current input given a windowed buffer
+                /// </summary>
+                /// <param name="input">A reference to the input object.</param>
+                /// <param name="windowedBuffer">A reference to the windowed buffer.</param>
+                /// <param name="iteration">A long number that indicates the number of times ComputeRawAnomalyScore has been called so far (starting value = 0).</param>
+                /// <returns>The raw anomaly score for the input. The Assumption is the higher absolute value of the raw score, the more anomalous the input is.
+                /// The sign of the score determines whether it's a positive anomaly or a negative one.</returns>
+                private protected abstract Double ComputeRawAnomalyScore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration);
             }
         }
-    }
 }
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
index a13baf46ae..d476ba6c64 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
@@ -38,7 +38,7 @@ public DataBox(T value)
     /// <typeparam name="TInput">The input type of the sequential processing.</typeparam>
     /// <typeparam name="TOutput">The dst type of the sequential processing.</typeparam>
     /// <typeparam name="TState">The state type of the sequential processing. Must be a class inherited from StateBase </typeparam>
-    public abstract class SequentialTransformBase<TInput, TOutput, TState> : TransformBase
+    internal abstract class SequentialTransformBase<TInput, TOutput, TState> : TransformBase
        where TState : SequentialTransformBase<TInput, TOutput, TState>.StateBase, new()
     {
         /// <summary>
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
index 31a0b57ea4..e0878e58e1 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
@@ -18,20 +18,22 @@
 
 namespace Microsoft.ML.TimeSeriesProcessing
 {
+
     /// <summary>
     /// The base class for sequential processing transforms. This class implements the basic sliding window buffering. The derived classes need to specify the transform logic,
     /// the initialization logic and the learning logic via implementing the abstract methods TransformCore(), InitializeStateCore() and LearnStateFromDataCore(), respectively
     /// </summary>
     /// <typeparam name="TInput">The input type of the sequential processing.</typeparam>
     /// <typeparam name="TOutput">The dst type of the sequential processing.</typeparam>
-    /// <typeparam name="TState">The state type of the sequential processing. Must be a class inherited from StateBase </typeparam>
-    public abstract class SequentialTransformerBase<TInput, TOutput, TState> : IStatefulTransformer
-       where TState : SequentialTransformerBase<TInput, TOutput, TState>.StateBase, new()
+    /// <typeparam name="TState">The dst type of the sequential processing.</typeparam>
+    internal abstract class SequentialTransformerBase<TInput, TOutput, TState> : IStatefulTransformer
+        where TState : SequentialTransformerBase<TInput, TOutput, TState>.StateBase, new()
     {
+
         /// <summary>
         /// The base class for encapsulating the State object for sequential processing. This class implements a windowed buffer.
         /// </summary>
-        public abstract class StateBase
+        internal class StateBase
         {
             // Ideally this class should be private. However, due to the current constraints with the LambdaTransform, we need to have
             // access to the state class when inheriting from SequentialTransformerBase.
@@ -61,7 +63,7 @@ public abstract class StateBase
             /// </summary>
             protected long RowCounter { get; private set; }
 
-            private protected StateBase()
+            public StateBase()
             {
             }
 
@@ -204,7 +206,7 @@ public void ProcessWithoutBuffer(ref TInput input, ref TOutput output)
             /// The abstract method that specifies the NA value for the dst type.
             /// </summary>
             /// <returns></returns>
-            private protected abstract void SetNaOutput(ref TOutput dst);
+            private protected virtual void SetNaOutput(ref TOutput dst) { }
 
             /// <summary>
             /// The abstract method that realizes the main logic for the transform.
@@ -213,41 +215,52 @@ public void ProcessWithoutBuffer(ref TInput input, ref TOutput output)
             /// <param name="dst">A reference to the dst object.</param>
             /// <param name="windowedBuffer">A reference to the windowed buffer.</param>
             /// <param name="iteration">A long number that indicates the number of times TransformCore has been called so far (starting value = 0).</param>
-            private protected abstract void TransformCore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration, ref TOutput dst);
+            private protected virtual void TransformCore(ref TInput input, FixedSizeQueue<TInput> windowedBuffer, long iteration, ref TOutput dst)
+            {
+
+            }
 
             /// <summary>
             /// The abstract method that realizes the logic for initializing the state object.
             /// </summary>
-            private protected abstract void InitializeStateCore(bool disk = false);
+            private protected virtual void InitializeStateCore(bool disk = false)
+            {
+
+            }
 
             /// <summary>
             /// The abstract method that realizes the logic for learning the parameters and the initial state object from data.
             /// </summary>
             /// <param name="data">A queue of data points used for training</param>
-            private protected abstract void LearnStateFromDataCore(FixedSizeQueue<TInput> data);
+            private protected virtual void LearnStateFromDataCore(FixedSizeQueue<TInput> data)
+            {
+            }
 
-            public abstract void Consume(TInput value);
+            public virtual void Consume(TInput value)
+            {
+
+            }
 
-            public StateBase Clone()
+            public TState Clone()
             {
-                var clone = (StateBase)MemberwiseClone();
+                var clone = (TState)MemberwiseClone();
                 CloneCore(clone);
                 return clone;
             }
 
-            private protected virtual void CloneCore(StateBase state)
+            private protected virtual void CloneCore(TState state)
             {
                 state.WindowedBuffer = WindowedBuffer.Clone();
                 state.InitialWindowedBuffer = InitialWindowedBuffer.Clone();
             }
         }
 
-        private protected readonly IHost Host;
+        internal readonly IHost Host;
 
         /// <summary>
         /// The window size for buffering.
         /// </summary>
-        private protected readonly int WindowSize;
+        internal readonly int WindowSize;
 
         /// <summary>
         /// The number of datapoints from the beginning of the sequence that are used for learning the initial state.
@@ -260,7 +273,7 @@ private protected virtual void CloneCore(StateBase state)
 
         public bool IsRowToRowMapper => false;
 
-        public TState StateRef { get; set; }
+        internal TState StateRef { get; set; }
 
         public int StateRefCount;
 
@@ -346,9 +359,9 @@ private protected virtual void SaveModel(ModelSaveContext ctx)
 
         public abstract Schema GetOutputSchema(Schema inputSchema);
 
-        private protected abstract IStatefulRowMapper MakeRowMapper(Schema schema);
+        internal abstract IStatefulRowMapper MakeRowMapper(Schema schema);
 
-        private protected SequentialDataTransform MakeDataTransform(IDataView input)
+        internal SequentialDataTransform MakeDataTransform(IDataView input)
         {
             Host.CheckValue(input, nameof(input));
             return new SequentialDataTransform(Host, this, input, MakeRowMapper(input.Schema));
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
index ca94f2c788..3f57b81113 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
@@ -19,7 +19,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// <summary>
     /// Outputs a sliding window on a time series of type Single.
     /// </summary>
-    public sealed class SlidingWindowTransform : SlidingWindowTransformBase<Single>
+    internal sealed class SlidingWindowTransform : SlidingWindowTransformBase<Single>
     {
         public const string Summary = "Returns the last values for a time series [y(t-d-l+1), y(t-d-l+2), ..., y(t-l-1), y(t-l)] where d is the size of the window, l the lag and y is a Float.";
         public const string LoaderSignature = "SlideWinTransform";
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
index 05b01a0909..84abfacc94 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
@@ -22,7 +22,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// and l is the delay.
     /// </summary>
 
-    public abstract class SlidingWindowTransformBase<TInput> : SequentialTransformBase<TInput, VBuffer<TInput>, SlidingWindowTransformBase<TInput>.StateSlide>
+    internal abstract class SlidingWindowTransformBase<TInput> : SequentialTransformBase<TInput, VBuffer<TInput>, SlidingWindowTransformBase<TInput>.StateSlide>
     {
         /// <summary>
         /// Defines what should be done about the first rows.
diff --git a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
index 857b21bcdf..4cb858485a 100644
--- a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
@@ -6,6 +6,7 @@
 using System.IO;
 using Microsoft.Data.DataView;
 using Microsoft.ML.CommandLine;
+using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
@@ -13,24 +14,24 @@
 
 namespace Microsoft.ML.TimeSeriesProcessing
 {
+    public enum ErrorFunction : byte
+    {
+        SignedDifference,
+        AbsoluteDifference,
+        SignedProportion,
+        AbsoluteProportion,
+        SquaredDifference
+    }
+
     /// <summary>
     /// Provides the utility functions for different error functions for computing deviation.
     /// </summary>
-    public static class ErrorFunctionUtils
+    internal static class ErrorFunctionUtils
     {
         public const string ErrorFunctionHelpText = "The error function should be either (0) SignedDifference, (1) AbsoluteDifference, (2) SignedProportion" +
                                                      " (3) AbsoluteProportion or (4) SquaredDifference.";
 
-        public enum ErrorFunction : byte
-        {
-            SignedDifference,
-            AbsoluteDifference,
-            SignedProportion,
-            AbsoluteProportion,
-            SquaredDifference
-        }
-
-        public static Double SignedDifference(Double actual, Double predicted)
+        public static double SignedDifference(double actual, Double predicted)
         {
             return actual - predicted;
         }
@@ -82,12 +83,70 @@ public static Func<Double, Double, Double> GetErrorFunction(ErrorFunction errorF
     }
 
     /// <summary>
-    /// This base class that implements the general anomaly detection transform based on Singular Spectrum modeling of the time-series.
+    /// The wrapper to <see cref="SsaAnomalyDetectionBase"/> that implements the general anomaly detection transform based on Singular Spectrum modeling of the time-series.
     /// For the details of the Singular Spectrum Analysis (SSA), refer to http://arxiv.org/pdf/1206.6910.pdf.
     /// </summary>
-    public abstract class SsaAnomalyDetectionBase : SequentialAnomalyDetectionTransformBase<Single, SsaAnomalyDetectionBase.State>
+    public class SsaAnomalyDetectionBaseWrapper : IStatefulTransformer, ICanSaveModel
     {
-        public abstract class SsaArguments : ArgumentsBase
+        /// <summary>
+        /// Whether a call to <see cref="GetRowToRowMapper(Schema)"/> should succeed, on an
+        /// appropriate schema.
+        /// </summary>
+        public bool IsRowToRowMapper => InternalTransform.IsRowToRowMapper;
+
+        /// <summary>
+        /// Creates a clone of the transfomer. Used for taking the snapshot of the state.
+        /// </summary>
+        /// <returns></returns>
+        IStatefulTransformer IStatefulTransformer.Clone() => InternalTransform.Clone();
+
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
+        public Schema GetOutputSchema(Schema inputSchema) => InternalTransform.GetOutputSchema(inputSchema);
+
+        /// <summary>
+        /// Constructs a row-to-row mapper based on an input schema. If <see cref="IsRowToRowMapper"/>
+        /// is <c>false</c>, then an exception should be thrown. If the input schema is in any way
+        /// unsuitable for constructing the mapper, an exception should likewise be thrown.
+        /// </summary>
+        /// <param name="inputSchema">The input schema for which we should get the mapper.</param>
+        /// <returns>The row to row mapper.</returns>
+        public IRowToRowMapper GetRowToRowMapper(Schema inputSchema) => InternalTransform.GetRowToRowMapper(inputSchema);
+
+        /// <summary>
+        /// Same as <see cref="ITransformer.GetRowToRowMapper(Schema)"/> but also supports mechanism to save the state.
+        /// </summary>
+        /// <param name="inputSchema">The input schema for which we should get the mapper.</param>
+        /// <returns>The row to row mapper.</returns>
+        public IRowToRowMapper GetStatefulRowToRowMapper(Schema inputSchema) => ((IStatefulTransformer)InternalTransform).GetStatefulRowToRowMapper(inputSchema);
+
+        /// <summary>
+        /// Take the data in, make transformations, output the data.
+        /// Note that <see cref="IDataView"/>'s are lazy, so no actual transformations happen here, just schema validation.
+        /// </summary>
+        public IDataView Transform(IDataView input) => InternalTransform.Transform(input);
+
+        /// <summary>
+        /// For saving a model into a repository.
+        /// </summary>
+        public virtual void Save(ModelSaveContext ctx) => InternalTransform.SaveThis(ctx);
+
+        /// <summary>
+        /// Creates a row mapper from Schema.
+        /// </summary>
+        internal IStatefulRowMapper MakeRowMapper(Schema schema) => InternalTransform.MakeRowMapper(schema);
+
+        /// <summary>
+        /// Creates an IDataTransform from an IDataView.
+        /// </summary>
+        internal IDataTransform MakeDataTransform(IDataView input) => InternalTransform.MakeDataTransform(input);
+
+        /// <summary>
+        /// Options for SSA Anomaly Detection.
+        /// </summary>
+        internal abstract class SsaOptions : ArgumentsBase
         {
             [Argument(ArgumentType.Required, HelpText = "The inner window size for SSA in [2, windowSize]", ShortName = "swnd", SortOrder = 11)]
             public int SeasonalWindowSize;
@@ -96,178 +155,204 @@ public abstract class SsaArguments : ArgumentsBase
             public Single DiscountFactor = 1;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The function used to compute the error between the expected and the observed value", ShortName = "err", SortOrder = 13)]
-            public ErrorFunctionUtils.ErrorFunction ErrorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference;
+            public ErrorFunction ErrorFunction = ErrorFunction.SignedDifference;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The flag determing whether the model is adaptive", ShortName = "adp", SortOrder = 14)]
             public bool IsAdaptive = false;
         }
 
-        protected readonly int SeasonalWindowSize;
-        protected readonly Single DiscountFactor;
-        protected readonly bool IsAdaptive;
-        protected readonly ErrorFunctionUtils.ErrorFunction ErrorFunction;
-        protected readonly Func<Double, Double, Double> ErrorFunc;
-        protected SequenceModelerBase<Single, Single> Model;
+        internal SsaAnomalyDetectionBase InternalTransform;
 
-        public SsaAnomalyDetectionBase(SsaArguments args, string name, IHostEnvironment env)
-            : base(args.WindowSize, 0, args.Source, args.Name, name, env, args.Side, args.Martingale, args.AlertOn, args.PowerMartingaleEpsilon, args.AlertThreshold)
+        internal SsaAnomalyDetectionBaseWrapper(SsaOptions options, string name, IHostEnvironment env)
         {
-            Host.CheckUserArg(2 <= args.SeasonalWindowSize, nameof(args.SeasonalWindowSize), "Must be at least 2.");
-            Host.CheckUserArg(0 <= args.DiscountFactor && args.DiscountFactor <= 1, nameof(args.DiscountFactor), "Must be in the range [0, 1].");
-            Host.CheckUserArg(Enum.IsDefined(typeof(ErrorFunctionUtils.ErrorFunction), args.ErrorFunction), nameof(args.ErrorFunction), ErrorFunctionUtils.ErrorFunctionHelpText);
-
-            SeasonalWindowSize = args.SeasonalWindowSize;
-            DiscountFactor = args.DiscountFactor;
-            ErrorFunction = args.ErrorFunction;
-            ErrorFunc = ErrorFunctionUtils.GetErrorFunction(ErrorFunction);
-            IsAdaptive = args.IsAdaptive;
-            // Creating the master SSA model
-            Model = new AdaptiveSingularSpectrumSequenceModeler(Host, args.InitialWindowSize, SeasonalWindowSize + 1, SeasonalWindowSize,
-                DiscountFactor, AdaptiveSingularSpectrumSequenceModeler.RankSelectionMethod.Exact, null, SeasonalWindowSize / 2, false, false);
-
-            StateRef = new State();
-            StateRef.InitState(WindowSize, InitialWindowSize, this, Host);
+            InternalTransform = new SsaAnomalyDetectionBase(options, name, env, this);
         }
 
-        public SsaAnomalyDetectionBase(IHostEnvironment env, ModelLoadContext ctx, string name)
-            : base(env, ctx, name)
+        internal SsaAnomalyDetectionBaseWrapper(IHostEnvironment env, ModelLoadContext ctx, string name)
         {
-            // *** Binary format ***
-            // <base>
-            // int: _seasonalWindowSize
-            // float: _discountFactor
-            // byte: _errorFunction
-            // bool: _isAdaptive
-            // AdaptiveSingularSpectrumSequenceModeler: _model
-
-            Host.CheckDecode(InitialWindowSize == 0);
-
-            SeasonalWindowSize = ctx.Reader.ReadInt32();
-            Host.CheckDecode(2 <= SeasonalWindowSize);
-
-            DiscountFactor = ctx.Reader.ReadSingle();
-            Host.CheckDecode(0 <= DiscountFactor && DiscountFactor <= 1);
-
-            byte temp;
-            temp = ctx.Reader.ReadByte();
-            Host.CheckDecode(Enum.IsDefined(typeof(ErrorFunctionUtils.ErrorFunction), temp));
-            ErrorFunction = (ErrorFunctionUtils.ErrorFunction)temp;
-            ErrorFunc = ErrorFunctionUtils.GetErrorFunction(ErrorFunction);
-
-            IsAdaptive = ctx.Reader.ReadBoolean();
-            StateRef = new State(ctx.Reader);
-
-            ctx.LoadModel<SequenceModelerBase<Single, Single>, SignatureLoadModel>(env, out Model, "SSA");
-            Host.CheckDecode(Model != null);
-            StateRef.InitState(this, Host);
+            InternalTransform = new SsaAnomalyDetectionBase(env, ctx, name);
         }
 
-        public override Schema GetOutputSchema(Schema inputSchema)
+        /// <summary>
+        /// This base class that implements the general anomaly detection transform based on Singular Spectrum modeling of the time-series.
+        /// For the details of the Singular Spectrum Analysis (SSA), refer to http://arxiv.org/pdf/1206.6910.pdf.
+        /// </summary>
+        internal sealed class SsaAnomalyDetectionBase : SequentialAnomalyDetectionTransformBase<float, SsaAnomalyDetectionBase.State>
         {
-            Host.CheckValue(inputSchema, nameof(inputSchema));
-
-            if (!inputSchema.TryGetColumnIndex(InputColumnName, out var col))
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName);
+            internal SsaAnomalyDetectionBaseWrapper Parent;
+            internal readonly int SeasonalWindowSize;
+            internal readonly Single DiscountFactor;
+            internal readonly bool IsAdaptive;
+            internal readonly ErrorFunction ErrorFunction;
+            internal readonly Func<Double, Double, Double> ErrorFunc;
+            internal SequenceModelerBase<Single, Single> Model;
+
+            public SsaAnomalyDetectionBase(SsaOptions options, string name, IHostEnvironment env, SsaAnomalyDetectionBaseWrapper parent)
+                : base(options.WindowSize, 0, options.Source, options.Name, name, env, options.Side, options.Martingale, options.AlertOn, options.PowerMartingaleEpsilon, options.AlertThreshold)
+            {
+                Host.CheckUserArg(2 <= options.SeasonalWindowSize, nameof(options.SeasonalWindowSize), "Must be at least 2.");
+                Host.CheckUserArg(0 <= options.DiscountFactor && options.DiscountFactor <= 1, nameof(options.DiscountFactor), "Must be in the range [0, 1].");
+                Host.CheckUserArg(Enum.IsDefined(typeof(ErrorFunction), options.ErrorFunction), nameof(options.ErrorFunction), ErrorFunctionUtils.ErrorFunctionHelpText);
+
+                SeasonalWindowSize = options.SeasonalWindowSize;
+                DiscountFactor = options.DiscountFactor;
+                ErrorFunction = options.ErrorFunction;
+                ErrorFunc = ErrorFunctionUtils.GetErrorFunction(ErrorFunction);
+                IsAdaptive = options.IsAdaptive;
+                // Creating the master SSA model
+                Model = new AdaptiveSingularSpectrumSequenceModeler(Host, options.InitialWindowSize, SeasonalWindowSize + 1, SeasonalWindowSize,
+                    DiscountFactor, AdaptiveSingularSpectrumSequenceModeler.RankSelectionMethod.Exact, null, SeasonalWindowSize / 2, false, false);
+
+                StateRef = new State();
+                StateRef.InitState(WindowSize, InitialWindowSize, this, Host);
+                Parent = parent;
+            }
 
-            var colType = inputSchema[col].Type;
-            if (colType != NumberType.R4)
-                throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName, "float", colType.ToString());
+            public SsaAnomalyDetectionBase(IHostEnvironment env, ModelLoadContext ctx, string name)
+                : base(env, ctx, name)
+            {
+                // *** Binary format ***
+                // <base>
+                // int: _seasonalWindowSize
+                // float: _discountFactor
+                // byte: _errorFunction
+                // bool: _isAdaptive
+                // AdaptiveSingularSpectrumSequenceModeler: _model
+
+                Host.CheckDecode(InitialWindowSize == 0);
+
+                SeasonalWindowSize = ctx.Reader.ReadInt32();
+                Host.CheckDecode(2 <= SeasonalWindowSize);
+
+                DiscountFactor = ctx.Reader.ReadSingle();
+                Host.CheckDecode(0 <= DiscountFactor && DiscountFactor <= 1);
+
+                byte temp;
+                temp = ctx.Reader.ReadByte();
+                Host.CheckDecode(Enum.IsDefined(typeof(ErrorFunction), temp));
+                ErrorFunction = (ErrorFunction)temp;
+                ErrorFunc = ErrorFunctionUtils.GetErrorFunction(ErrorFunction);
+
+                IsAdaptive = ctx.Reader.ReadBoolean();
+                StateRef = new State(ctx.Reader);
+
+                ctx.LoadModel<SequenceModelerBase<Single, Single>, SignatureLoadModel>(env, out Model, "SSA");
+                Host.CheckDecode(Model != null);
+                StateRef.InitState(this, Host);
+            }
 
-            return Transform(new EmptyDataView(Host, inputSchema)).Schema;
-        }
+            public override Schema GetOutputSchema(Schema inputSchema)
+            {
+                Host.CheckValue(inputSchema, nameof(inputSchema));
 
-        private protected override void SaveModel(ModelSaveContext ctx)
-        {
-            Host.CheckValue(ctx, nameof(ctx));
-            ctx.CheckAtModel();
-
-            Host.Assert(InitialWindowSize == 0);
-            Host.Assert(2 <= SeasonalWindowSize);
-            Host.Assert(0 <= DiscountFactor && DiscountFactor <= 1);
-            Host.Assert(Enum.IsDefined(typeof(ErrorFunctionUtils.ErrorFunction), ErrorFunction));
-            Host.Assert(Model != null);
-
-            // *** Binary format ***
-            // <base>
-            // int: _seasonalWindowSize
-            // float: _discountFactor
-            // byte: _errorFunction
-            // bool: _isAdaptive
-            // State: StateRef
-            // AdaptiveSingularSpectrumSequenceModeler: _model
-
-            base.SaveModel(ctx);
-            ctx.Writer.Write(SeasonalWindowSize);
-            ctx.Writer.Write(DiscountFactor);
-            ctx.Writer.Write((byte)ErrorFunction);
-            ctx.Writer.Write(IsAdaptive);
-            StateRef.Save(ctx.Writer);
-
-            ctx.SaveModel(Model, "SSA");
-        }
+                if (!inputSchema.TryGetColumnIndex(InputColumnName, out var col))
+                    throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName);
 
-        public sealed class State : AnomalyDetectionStateBase
-        {
-            private SequenceModelerBase<Single, Single> _model;
-            private SsaAnomalyDetectionBase _parentAnomalyDetector;
+                var colType = inputSchema[col].Type;
+                if (colType != NumberType.R4)
+                    throw Host.ExceptSchemaMismatch(nameof(inputSchema), "input", InputColumnName, "float", colType.ToString());
 
-            public State()
-            {
+                return Transform(new EmptyDataView(Host, inputSchema)).Schema;
             }
 
-            internal State(BinaryReader reader) : base(reader)
+            private protected override void SaveModel(ModelSaveContext ctx)
             {
-                WindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
-                InitialWindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                Parent.Save(ctx);
             }
 
-            internal override void Save(BinaryWriter writer)
+            internal void SaveThis(ModelSaveContext ctx)
             {
-                base.Save(writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(WindowedBuffer, writer);
-                TimeSeriesUtils.SerializeFixedSizeQueue(InitialWindowedBuffer, writer);
+                Host.CheckValue(ctx, nameof(ctx));
+                ctx.CheckAtModel();
+
+                Host.Assert(InitialWindowSize == 0);
+                Host.Assert(2 <= SeasonalWindowSize);
+                Host.Assert(0 <= DiscountFactor && DiscountFactor <= 1);
+                Host.Assert(Enum.IsDefined(typeof(ErrorFunction), ErrorFunction));
+                Host.Assert(Model != null);
+
+                // *** Binary format ***
+                // <base>
+                // int: _seasonalWindowSize
+                // float: _discountFactor
+                // byte: _errorFunction
+                // bool: _isAdaptive
+                // State: StateRef
+                // AdaptiveSingularSpectrumSequenceModeler: _model
+
+                base.SaveModel(ctx);
+                ctx.Writer.Write(SeasonalWindowSize);
+                ctx.Writer.Write(DiscountFactor);
+                ctx.Writer.Write((byte)ErrorFunction);
+                ctx.Writer.Write(IsAdaptive);
+                StateRef.Save(ctx.Writer);
+
+                ctx.SaveModel(Model, "SSA");
             }
 
-            private protected override void CloneCore(StateBase state)
+            internal sealed class State : AnomalyDetectionStateBase
             {
-                base.CloneCore(state);
-                Contracts.Assert(state is State);
-                var stateLocal = state as State;
-                stateLocal.WindowedBuffer = WindowedBuffer.Clone();
-                stateLocal.InitialWindowedBuffer = InitialWindowedBuffer.Clone();
-                if (_model != null)
+                private SequenceModelerBase<Single, Single> _model;
+                private SsaAnomalyDetectionBase _parentAnomalyDetector;
+
+                public State()
                 {
-                    _parentAnomalyDetector.Model = _parentAnomalyDetector.Model.Clone();
-                    _model = _parentAnomalyDetector.Model;
                 }
-            }
 
-            private protected override void LearnStateFromDataCore(FixedSizeQueue<Single> data)
-            {
-                // This method is empty because there is no need to implement a training logic here.
-            }
+                internal State(BinaryReader reader) : base(reader)
+                {
+                    WindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                    InitialWindowedBuffer = TimeSeriesUtils.DeserializeFixedSizeQueueSingle(reader, Host);
+                }
 
-            private protected override void InitializeAnomalyDetector()
-            {
-                _parentAnomalyDetector = (SsaAnomalyDetectionBase)Parent;
-                _model = _parentAnomalyDetector.Model;
-            }
+                internal override void Save(BinaryWriter writer)
+                {
+                    base.Save(writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(WindowedBuffer, writer);
+                    TimeSeriesUtils.SerializeFixedSizeQueue(InitialWindowedBuffer, writer);
+                }
 
-            private protected override double ComputeRawAnomalyScore(ref Single input, FixedSizeQueue<Single> windowedBuffer, long iteration)
-            {
-                // Get the prediction for the next point opn the series
-                Single expectedValue = 0;
-                _model.PredictNext(ref expectedValue);
+                private protected override void CloneCore(State state)
+                {
+                    base.CloneCore(state);
+                    Contracts.Assert(state is State);
+                    var stateLocal = state as State;
+                    stateLocal.WindowedBuffer = WindowedBuffer.Clone();
+                    stateLocal.InitialWindowedBuffer = InitialWindowedBuffer.Clone();
+                    if (_model != null)
+                    {
+                        _parentAnomalyDetector.Model = _parentAnomalyDetector.Model.Clone();
+                        _model = _parentAnomalyDetector.Model;
+                    }
+                }
 
-                if (PreviousPosition == -1)
-                    // Feed the current point to the model
-                    _model.Consume(ref input, _parentAnomalyDetector.IsAdaptive);
+                private protected override void LearnStateFromDataCore(FixedSizeQueue<Single> data)
+                {
+                    // This method is empty because there is no need to implement a training logic here.
+                }
 
-                // Return the error as the raw anomaly score
-                return _parentAnomalyDetector.ErrorFunc(input, expectedValue);
-            }
+                private protected override void InitializeAnomalyDetector()
+                {
+                    _parentAnomalyDetector = (SsaAnomalyDetectionBase)Parent;
+                    _model = _parentAnomalyDetector.Model;
+                }
+
+                private protected override double ComputeRawAnomalyScore(ref Single input, FixedSizeQueue<Single> windowedBuffer, long iteration)
+                {
+                    // Get the prediction for the next point opn the series
+                    Single expectedValue = 0;
+                    _model.PredictNext(ref expectedValue);
+
+                    if (PreviousPosition == -1)
+                        // Feed the current point to the model
+                        _model.Consume(ref input, _parentAnomalyDetector.IsAdaptive);
 
-            public override void Consume(Single input) => _model.Consume(ref input, _parentAnomalyDetector.IsAdaptive);
+                    // Return the error as the raw anomaly score
+                    return _parentAnomalyDetector.ErrorFunc(input, expectedValue);
+                }
+
+                public override void Consume(Single input) => _model.Consume(ref input, _parentAnomalyDetector.IsAdaptive);
+            }
         }
     }
 }
diff --git a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
index a90029b4a6..6da61935dc 100644
--- a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
@@ -14,9 +14,8 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.TimeSeries;
 using Microsoft.ML.TimeSeriesProcessing;
-using static Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<System.Single, Microsoft.ML.TimeSeriesProcessing.SsaAnomalyDetectionBase.State>;
 
-[assembly: LoadableClass(SsaChangePointDetector.Summary, typeof(IDataTransform), typeof(SsaChangePointDetector), typeof(SsaChangePointDetector.Arguments), typeof(SignatureDataTransform),
+[assembly: LoadableClass(SsaChangePointDetector.Summary, typeof(IDataTransform), typeof(SsaChangePointDetector), typeof(SsaChangePointDetector.Options), typeof(SignatureDataTransform),
     SsaChangePointDetector.UserName, SsaChangePointDetector.LoaderSignature, SsaChangePointDetector.ShortName)]
 
 [assembly: LoadableClass(SsaChangePointDetector.Summary, typeof(IDataTransform), typeof(SsaChangePointDetector), null, typeof(SignatureLoadDataTransform),
@@ -34,14 +33,14 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// This class implements the change point detector transform based on Singular Spectrum modeling of the time-series.
     /// For the details of the Singular Spectrum Analysis (SSA), refer to http://arxiv.org/pdf/1206.6910.pdf.
     /// </summary>
-    public sealed class SsaChangePointDetector : SsaAnomalyDetectionBase
+    public sealed class SsaChangePointDetector : SsaAnomalyDetectionBaseWrapper, IStatefulTransformer
     {
         internal const string Summary = "This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).";
         internal const string LoaderSignature = "SsaChangePointDetector";
         internal const string UserName = "SSA Change Point Detection";
         internal const string ShortName = "chgpnt";
 
-        public sealed class Arguments : TransformInputBase
+        internal sealed class Options : TransformInputBase
         {
             [Argument(ArgumentType.Required, HelpText = "The name of the source column.", ShortName = "src",
                 SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
@@ -67,32 +66,32 @@ public sealed class Arguments : TransformInputBase
             public int SeasonalWindowSize = 10;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The function used to compute the error between the expected and the observed value.", ShortName = "err", SortOrder = 103)]
-            public ErrorFunctionUtils.ErrorFunction ErrorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference;
+            public ErrorFunction ErrorFunction = ErrorFunction.SignedDifference;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The martingale used for scoring.", ShortName = "mart", SortOrder = 104)]
-            public MartingaleType Martingale = SequentialAnomalyDetectionTransformBase<float, State>.MartingaleType.Power;
+            public MartingaleType Martingale = MartingaleType.Power;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The epsilon parameter for the Power martingale.",
                 ShortName = "eps", SortOrder = 105)]
             public double PowerMartingaleEpsilon = 0.1;
         }
 
-        private sealed class BaseArguments : SsaArguments
+        private sealed class BaseArguments : SsaOptions
         {
-            public BaseArguments(Arguments args)
+            public BaseArguments(Options options)
             {
-                Source = args.Source;
-                Name = args.Name;
-                Side = SequentialAnomalyDetectionTransformBase<float, State>.AnomalySide.TwoSided;
-                WindowSize = args.ChangeHistoryLength;
-                InitialWindowSize = args.TrainingWindowSize;
-                SeasonalWindowSize = args.SeasonalWindowSize;
-                Martingale = args.Martingale;
-                PowerMartingaleEpsilon = args.PowerMartingaleEpsilon;
-                AlertOn = SequentialAnomalyDetectionTransformBase<float, State>.AlertingScore.MartingaleScore;
+                Source = options.Source;
+                Name = options.Name;
+                Side = AnomalySide.TwoSided;
+                WindowSize = options.ChangeHistoryLength;
+                InitialWindowSize = options.TrainingWindowSize;
+                SeasonalWindowSize = options.SeasonalWindowSize;
+                Martingale = options.Martingale;
+                PowerMartingaleEpsilon = options.PowerMartingaleEpsilon;
+                AlertOn = AlertingScore.MartingaleScore;
                 DiscountFactor = 1;
                 IsAdaptive = false;
-                ErrorFunction = args.ErrorFunction;
+                ErrorFunction = options.ErrorFunction;
             }
         }
 
@@ -106,48 +105,48 @@ private static VersionInfo GetVersionInfo()
                 loaderAssemblyName: typeof(SsaChangePointDetector).Assembly.FullName);
         }
 
-        internal SsaChangePointDetector(IHostEnvironment env, Arguments args, IDataView input)
-            : this(env, args)
+        internal SsaChangePointDetector(IHostEnvironment env, Options options, IDataView input)
+            : this(env, options)
         {
-            Model.Train(new RoleMappedData(input, null, InputColumnName));
+            InternalTransform.Model.Train(new RoleMappedData(input, null, InternalTransform.InputColumnName));
         }
 
         // Factory method for SignatureDataTransform.
-        private static IDataTransform Create(IHostEnvironment env, Arguments args, IDataView input)
+        private static IDataTransform Create(IHostEnvironment env, Options options, IDataView input)
         {
             Contracts.CheckValue(env, nameof(env));
-            env.CheckValue(args, nameof(args));
+            env.CheckValue(options, nameof(options));
             env.CheckValue(input, nameof(input));
 
-            return new SsaChangePointDetector(env, args, input).MakeDataTransform(input);
+            return new SsaChangePointDetector(env, options, input).MakeDataTransform(input);
         }
 
-        internal override IStatefulTransformer Clone()
+        IStatefulTransformer IStatefulTransformer.Clone()
         {
             var clone = (SsaChangePointDetector)MemberwiseClone();
-            clone.Model = clone.Model.Clone();
-            clone.StateRef = (State)clone.StateRef.Clone();
-            clone.StateRef.InitState(clone, Host);
+            clone.InternalTransform.Model = clone.InternalTransform.Model.Clone();
+            clone.InternalTransform.StateRef = (SsaAnomalyDetectionBase.State)clone.InternalTransform.StateRef.Clone();
+            clone.InternalTransform.StateRef.InitState(clone.InternalTransform, InternalTransform.Host);
             return clone;
         }
 
-        internal SsaChangePointDetector(IHostEnvironment env, Arguments args)
-            : base(new BaseArguments(args), LoaderSignature, env)
+        internal SsaChangePointDetector(IHostEnvironment env, Options options)
+            : base(new BaseArguments(options), LoaderSignature, env)
         {
-            switch (Martingale)
+            switch (InternalTransform.Martingale)
             {
                 case MartingaleType.None:
-                    AlertThreshold = Double.MaxValue;
+                    InternalTransform.AlertThreshold = Double.MaxValue;
                     break;
                 case MartingaleType.Power:
-                    AlertThreshold = Math.Exp(WindowSize * LogPowerMartigaleBettingFunc(1 - args.Confidence / 100, PowerMartingaleEpsilon));
+                    InternalTransform.AlertThreshold = Math.Exp(InternalTransform.WindowSize * InternalTransform.LogPowerMartigaleBettingFunc(1 - options.Confidence / 100, InternalTransform.PowerMartingaleEpsilon));
                     break;
                 case MartingaleType.Mixture:
-                    AlertThreshold = Math.Exp(WindowSize * LogMixtureMartigaleBettingFunc(1 - args.Confidence / 100));
+                    InternalTransform.AlertThreshold = Math.Exp(InternalTransform.WindowSize * InternalTransform.LogMixtureMartigaleBettingFunc(1 - options.Confidence / 100));
                     break;
                 default:
-                    Host.Assert(!Enum.IsDefined(typeof(MartingaleType), Martingale));
-                    throw Host.ExceptUserArg(nameof(args.Martingale), "Value not defined.");
+                    InternalTransform.Host.Assert(!Enum.IsDefined(typeof(MartingaleType), InternalTransform.Martingale));
+                    throw InternalTransform.Host.ExceptUserArg(nameof(options.Martingale), "Value not defined.");
             }
         }
 
@@ -177,27 +176,27 @@ internal SsaChangePointDetector(IHostEnvironment env, ModelLoadContext ctx)
             // *** Binary format ***
             // <base>
 
-            Host.CheckDecode(ThresholdScore == AlertingScore.MartingaleScore);
-            Host.CheckDecode(Side == AnomalySide.TwoSided);
-            Host.CheckDecode(DiscountFactor == 1);
-            Host.CheckDecode(IsAdaptive == false);
+            InternalTransform.Host.CheckDecode(InternalTransform.ThresholdScore == AlertingScore.MartingaleScore);
+            InternalTransform.Host.CheckDecode(InternalTransform.Side == AnomalySide.TwoSided);
+            InternalTransform.Host.CheckDecode(InternalTransform.DiscountFactor == 1);
+            InternalTransform.Host.CheckDecode(InternalTransform.IsAdaptive == false);
         }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
+        public override void Save(ModelSaveContext ctx)
         {
-            Host.CheckValue(ctx, nameof(ctx));
+            InternalTransform.Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
 
-            Host.Assert(ThresholdScore == AlertingScore.MartingaleScore);
-            Host.Assert(Side == AnomalySide.TwoSided);
-            Host.Assert(DiscountFactor == 1);
-            Host.Assert(IsAdaptive == false);
+            InternalTransform.Host.Assert(InternalTransform.ThresholdScore == AlertingScore.MartingaleScore);
+            InternalTransform.Host.Assert(InternalTransform.Side == AnomalySide.TwoSided);
+            InternalTransform.Host.Assert(InternalTransform.DiscountFactor == 1);
+            InternalTransform.Host.Assert(InternalTransform.IsAdaptive == false);
 
             // *** Binary format ***
             // <base>
 
-            base.SaveModel(ctx);
+            base.Save(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
@@ -219,7 +218,7 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Sch
     public sealed class SsaChangePointEstimator : IEstimator<SsaChangePointDetector>
     {
         private readonly IHost _host;
-        private readonly SsaChangePointDetector.Arguments _args;
+        private readonly SsaChangePointDetector.Options _options;
 
         /// <summary>
         /// Create a new instance of <see cref="SsaChangePointEstimator"/>
@@ -235,16 +234,16 @@ public sealed class SsaChangePointEstimator : IEstimator<SsaChangePointDetector>
         /// <param name="errorFunction">The function used to compute the error between the expected and the observed value.</param>
         /// <param name="martingale">The martingale used for scoring.</param>
         /// <param name="eps">The epsilon parameter for the Power martingale.</param>
-        public SsaChangePointEstimator(IHostEnvironment env, string outputColumnName,
+        internal SsaChangePointEstimator(IHostEnvironment env, string outputColumnName,
             int confidence,
             int changeHistoryLength,
             int trainingWindowSize,
             int seasonalityWindowSize,
             string inputColumnName = null,
-            ErrorFunctionUtils.ErrorFunction errorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference,
+            ErrorFunction errorFunction = ErrorFunction.SignedDifference,
             MartingaleType martingale = MartingaleType.Power,
             double eps = 0.1)
-            : this(env, new SsaChangePointDetector.Arguments
+            : this(env, new SsaChangePointDetector.Options
                 {
                     Name = outputColumnName,
                     Source = inputColumnName ?? outputColumnName,
@@ -259,38 +258,45 @@ public SsaChangePointEstimator(IHostEnvironment env, string outputColumnName,
         {
         }
 
-        public SsaChangePointEstimator(IHostEnvironment env, SsaChangePointDetector.Arguments args)
+        internal SsaChangePointEstimator(IHostEnvironment env, SsaChangePointDetector.Options options)
         {
             Contracts.CheckValue(env, nameof(env));
             _host = env.Register(nameof(SsaChangePointEstimator));
 
-            _host.CheckNonEmpty(args.Name, nameof(args.Name));
-            _host.CheckNonEmpty(args.Source, nameof(args.Source));
+            _host.CheckNonEmpty(options.Name, nameof(options.Name));
+            _host.CheckNonEmpty(options.Source, nameof(options.Source));
 
-            _args = args;
+            _options = options;
         }
 
+        /// <summary>
+        /// Train and return a transformer.
+        /// </summary>
         public SsaChangePointDetector Fit(IDataView input)
         {
             _host.CheckValue(input, nameof(input));
-            return new SsaChangePointDetector(_host, _args, input);
+            return new SsaChangePointDetector(_host, _options, input);
         }
 
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
         public SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
             _host.CheckValue(inputSchema, nameof(inputSchema));
 
-            if (!inputSchema.TryFindColumn(_args.Source, out var col))
-                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _args.Source);
+            if (!inputSchema.TryFindColumn(_options.Source, out var col))
+                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _options.Source);
             if (col.ItemType != NumberType.R4)
-                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _args.Source, "float", col.GetTypeString());
+                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _options.Source, "float", col.GetTypeString());
 
             var metadata = new List<SchemaShape.Column>() {
                 new SchemaShape.Column(MetadataUtils.Kinds.SlotNames, SchemaShape.Column.VectorKind.Vector, TextType.Instance, false)
             };
             var resultDic = inputSchema.ToDictionary(x => x.Name);
-            resultDic[_args.Name] = new SchemaShape.Column(
-                _args.Name, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
+            resultDic[_options.Name] = new SchemaShape.Column(
+                _options.Name, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
 
             return new SchemaShape(resultDic.Values);
         }
diff --git a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
index 5c1af6ccd8..9a2ad25aab 100644
--- a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
@@ -13,9 +13,8 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.TimeSeries;
 using Microsoft.ML.TimeSeriesProcessing;
-using static Microsoft.ML.TimeSeriesProcessing.SequentialAnomalyDetectionTransformBase<System.Single, Microsoft.ML.TimeSeriesProcessing.SsaAnomalyDetectionBase.State>;
 
-[assembly: LoadableClass(SsaSpikeDetector.Summary, typeof(IDataTransform), typeof(SsaSpikeDetector), typeof(SsaSpikeDetector.Arguments), typeof(SignatureDataTransform),
+[assembly: LoadableClass(SsaSpikeDetector.Summary, typeof(IDataTransform), typeof(SsaSpikeDetector), typeof(SsaSpikeDetector.Options), typeof(SignatureDataTransform),
     SsaSpikeDetector.UserName, SsaSpikeDetector.LoaderSignature, SsaSpikeDetector.ShortName)]
 
 [assembly: LoadableClass(SsaSpikeDetector.Summary, typeof(IDataTransform), typeof(SsaSpikeDetector), null, typeof(SignatureLoadDataTransform),
@@ -33,14 +32,14 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// This class implements the spike detector transform based on Singular Spectrum modeling of the time-series.
     /// For the details of the Singular Spectrum Analysis (SSA), refer to http://arxiv.org/pdf/1206.6910.pdf.
     /// </summary>
-    public sealed class SsaSpikeDetector : SsaAnomalyDetectionBase
+    public sealed class SsaSpikeDetector : SsaAnomalyDetectionBaseWrapper, IStatefulTransformer
     {
         internal const string Summary = "This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).";
-        public const string LoaderSignature = "SsaSpikeDetector";
-        public const string UserName = "SSA Spike Detection";
-        public const string ShortName = "spike";
+        internal const string LoaderSignature = "SsaSpikeDetector";
+        internal const string UserName = "SSA Spike Detection";
+        internal const string ShortName = "spike";
 
-        public sealed class Arguments : TransformInputBase
+        internal sealed class Options : TransformInputBase
         {
             [Argument(ArgumentType.Required, HelpText = "The name of the source column.", ShortName = "src",
                 SortOrder = 1, Purpose = SpecialPurpose.ColumnName)]
@@ -70,24 +69,24 @@ public sealed class Arguments : TransformInputBase
             public int SeasonalWindowSize = 10;
 
             [Argument(ArgumentType.AtMostOnce, HelpText = "The function used to compute the error between the expected and the observed value.", ShortName = "err", SortOrder = 103)]
-            public ErrorFunctionUtils.ErrorFunction ErrorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference;
+            public ErrorFunction ErrorFunction = ErrorFunction.SignedDifference;
         }
 
-        private sealed class BaseArguments : SsaArguments
+        private sealed class BaseArguments : SsaOptions
         {
-            public BaseArguments(Arguments args)
+            public BaseArguments(Options options)
             {
-                Source = args.Source;
-                Name = args.Name;
-                Side = args.Side;
-                WindowSize = args.PvalueHistoryLength;
-                InitialWindowSize = args.TrainingWindowSize;
-                SeasonalWindowSize = args.SeasonalWindowSize;
-                AlertThreshold = 1 - args.Confidence / 100;
-                AlertOn = SequentialAnomalyDetectionTransformBase<float, State>.AlertingScore.PValueScore;
+                Source = options.Source;
+                Name = options.Name;
+                Side = options.Side;
+                WindowSize = options.PvalueHistoryLength;
+                InitialWindowSize = options.TrainingWindowSize;
+                SeasonalWindowSize = options.SeasonalWindowSize;
+                AlertThreshold = 1 - options.Confidence / 100;
+                AlertOn = AlertingScore.PValueScore;
                 DiscountFactor = 1;
                 IsAdaptive = false;
-                ErrorFunction = args.ErrorFunction;
+                ErrorFunction = options.ErrorFunction;
                 Martingale = MartingaleType.None;
             }
         }
@@ -103,24 +102,24 @@ private static VersionInfo GetVersionInfo()
                 loaderAssemblyName: typeof(SsaSpikeDetector).Assembly.FullName);
         }
 
-        internal SsaSpikeDetector(IHostEnvironment env, Arguments args, IDataView input)
-            : base(new BaseArguments(args), LoaderSignature, env)
+        internal SsaSpikeDetector(IHostEnvironment env, Options options, IDataView input)
+            : base(new BaseArguments(options), LoaderSignature, env)
         {
-            Model.Train(new RoleMappedData(input, null, InputColumnName));
+            InternalTransform.Model.Train(new RoleMappedData(input, null, InternalTransform.InputColumnName));
         }
 
         // Factory method for SignatureDataTransform.
-        private static IDataTransform Create(IHostEnvironment env, Arguments args, IDataView input)
+        private static IDataTransform Create(IHostEnvironment env, Options options, IDataView input)
         {
             Contracts.CheckValue(env, nameof(env));
-            env.CheckValue(args, nameof(args));
+            env.CheckValue(options, nameof(options));
             env.CheckValue(input, nameof(input));
 
-            return new SsaSpikeDetector(env, args, input).MakeDataTransform(input);
+            return new SsaSpikeDetector(env, options, input).MakeDataTransform(input);
         }
 
-        internal SsaSpikeDetector(IHostEnvironment env, Arguments args)
-            : base(new BaseArguments(args), LoaderSignature, env)
+        internal SsaSpikeDetector(IHostEnvironment env, Options options)
+            : base(new BaseArguments(options), LoaderSignature, env)
         {
             // This constructor is empty.
         }
@@ -135,12 +134,12 @@ private static IDataTransform Create(IHostEnvironment env, ModelLoadContext ctx,
             return new SsaSpikeDetector(env, ctx).MakeDataTransform(input);
         }
 
-        internal override IStatefulTransformer Clone()
+        IStatefulTransformer IStatefulTransformer.Clone()
         {
             var clone = (SsaSpikeDetector)MemberwiseClone();
-            clone.Model = clone.Model.Clone();
-            clone.StateRef = (State)clone.StateRef.Clone();
-            clone.StateRef.InitState(clone, Host);
+            clone.InternalTransform.Model = clone.InternalTransform.Model.Clone();
+            clone.InternalTransform.StateRef = (SsaAnomalyDetectionBase.State)clone.InternalTransform.StateRef.Clone();
+            clone.InternalTransform.StateRef.InitState(clone.InternalTransform, InternalTransform.Host);
             return clone;
         }
 
@@ -160,25 +159,25 @@ internal SsaSpikeDetector(IHostEnvironment env, ModelLoadContext ctx)
             // *** Binary format ***
             // <base>
 
-            Host.CheckDecode(ThresholdScore == AlertingScore.PValueScore);
-            Host.CheckDecode(DiscountFactor == 1);
-            Host.CheckDecode(IsAdaptive == false);
+            InternalTransform.Host.CheckDecode(InternalTransform.ThresholdScore == AlertingScore.PValueScore);
+            InternalTransform.Host.CheckDecode(InternalTransform.DiscountFactor == 1);
+            InternalTransform.Host.CheckDecode(InternalTransform.IsAdaptive == false);
         }
 
-        private protected override void SaveModel(ModelSaveContext ctx)
+        public override void Save(ModelSaveContext ctx)
         {
-            Host.CheckValue(ctx, nameof(ctx));
+            InternalTransform.Host.CheckValue(ctx, nameof(ctx));
             ctx.CheckAtModel();
             ctx.SetVersionInfo(GetVersionInfo());
 
-            Host.Assert(ThresholdScore == AlertingScore.PValueScore);
-            Host.Assert(DiscountFactor == 1);
-            Host.Assert(IsAdaptive == false);
+            InternalTransform.Host.Assert(InternalTransform.ThresholdScore == AlertingScore.PValueScore);
+            InternalTransform.Host.Assert(InternalTransform.DiscountFactor == 1);
+            InternalTransform.Host.Assert(InternalTransform.IsAdaptive == false);
 
             // *** Binary format ***
             // <base>
 
-            base.SaveModel(ctx);
+            base.Save(ctx);
         }
 
         // Factory method for SignatureLoadRowMapper.
@@ -200,7 +199,7 @@ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, Sch
     public sealed class SsaSpikeEstimator : IEstimator<SsaSpikeDetector>
     {
         private readonly IHost _host;
-        private readonly SsaSpikeDetector.Arguments _args;
+        private readonly SsaSpikeDetector.Options _options;
 
         /// <summary>
         /// Create a new instance of <see cref="SsaSpikeEstimator"/>
@@ -215,7 +214,7 @@ public sealed class SsaSpikeEstimator : IEstimator<SsaSpikeDetector>
         /// The vector contains Alert, Raw Score, P-Value as first three values.</param>
         /// <param name="side">The argument that determines whether to detect positive or negative anomalies, or both.</param>
         /// <param name="errorFunction">The function used to compute the error between the expected and the observed value.</param>
-        public SsaSpikeEstimator(IHostEnvironment env,
+        internal SsaSpikeEstimator(IHostEnvironment env,
             string outputColumnName,
             int confidence,
             int pvalueHistoryLength,
@@ -223,8 +222,8 @@ public SsaSpikeEstimator(IHostEnvironment env,
             int seasonalityWindowSize,
             string inputColumnName = null,
             AnomalySide side = AnomalySide.TwoSided,
-            ErrorFunctionUtils.ErrorFunction errorFunction = ErrorFunctionUtils.ErrorFunction.SignedDifference)
-            : this(env, new SsaSpikeDetector.Arguments
+            ErrorFunction errorFunction = ErrorFunction.SignedDifference)
+            : this(env, new SsaSpikeDetector.Options
                 {
                     Source = inputColumnName ?? outputColumnName,
                     Name = outputColumnName,
@@ -238,38 +237,45 @@ public SsaSpikeEstimator(IHostEnvironment env,
         {
         }
 
-        public SsaSpikeEstimator(IHostEnvironment env, SsaSpikeDetector.Arguments args)
+        internal SsaSpikeEstimator(IHostEnvironment env, SsaSpikeDetector.Options options)
         {
             Contracts.CheckValue(env, nameof(env));
             _host = env.Register(nameof(SsaSpikeEstimator));
 
-            _host.CheckNonEmpty(args.Name, nameof(args.Name));
-            _host.CheckNonEmpty(args.Source, nameof(args.Source));
+            _host.CheckNonEmpty(options.Name, nameof(options.Name));
+            _host.CheckNonEmpty(options.Source, nameof(options.Source));
 
-            _args = args;
+            _options = options;
         }
 
+        /// <summary>
+        /// Train and return a transformer.
+        /// </summary>
         public SsaSpikeDetector Fit(IDataView input)
         {
             _host.CheckValue(input, nameof(input));
-            return new SsaSpikeDetector(_host, _args, input);
+            return new SsaSpikeDetector(_host, _options, input);
         }
 
+        /// <summary>
+        /// Schema propagation for transformers.
+        /// Returns the output schema of the data, if the input schema is like the one provided.
+        /// </summary>
         public SchemaShape GetOutputSchema(SchemaShape inputSchema)
         {
             _host.CheckValue(inputSchema, nameof(inputSchema));
 
-            if (!inputSchema.TryFindColumn(_args.Source, out var col))
-                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _args.Source);
+            if (!inputSchema.TryFindColumn(_options.Source, out var col))
+                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _options.Source);
             if (col.ItemType != NumberType.R4)
-                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _args.Source, "float", col.GetTypeString());
+                throw _host.ExceptSchemaMismatch(nameof(inputSchema), "input", _options.Source, "float", col.GetTypeString());
 
             var metadata = new List<SchemaShape.Column>() {
                 new SchemaShape.Column(MetadataUtils.Kinds.SlotNames, SchemaShape.Column.VectorKind.Vector, TextType.Instance, false)
             };
             var resultDic = inputSchema.ToDictionary(x => x.Name);
-            resultDic[_args.Name] = new SchemaShape.Column(
-                _args.Name, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
+            resultDic[_options.Name] = new SchemaShape.Column(
+                _options.Name, SchemaShape.Column.VectorKind.Vector, NumberType.R8, false, new SchemaShape(metadata));
 
             return new SchemaShape(resultDic.Values);
         }
diff --git a/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs b/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
index 9e4564baa7..da5c7b9153 100644
--- a/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
+++ b/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
@@ -15,7 +15,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     internal static class TimeSeriesProcessingEntryPoints
     {
         [TlcModule.EntryPoint(Desc = ExponentialAverageTransform.Summary, UserName = ExponentialAverageTransform.UserName, ShortName = ExponentialAverageTransform.ShortName)]
-        public static CommonOutputs.TransformOutput ExponentialAverage(IHostEnvironment env, ExponentialAverageTransform.Arguments input)
+        internal static CommonOutputs.TransformOutput ExponentialAverage(IHostEnvironment env, ExponentialAverageTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "ExponentialAverageTransform", input);
             var xf = new ExponentialAverageTransform(h, input, input.Data);
@@ -27,31 +27,31 @@ public static CommonOutputs.TransformOutput ExponentialAverage(IHostEnvironment
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.IidChangePointDetector.Summary, UserName = TimeSeriesProcessing.IidChangePointDetector.UserName, ShortName = TimeSeriesProcessing.IidChangePointDetector.ShortName)]
-        public static CommonOutputs.TransformOutput IidChangePointDetector(IHostEnvironment env, IidChangePointDetector.Arguments input)
+        internal static CommonOutputs.TransformOutput IidChangePointDetector(IHostEnvironment env, IidChangePointDetector.Options options)
         {
-            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidChangePointDetector", input);
-            var view = new IidChangePointEstimator(h, input).Fit(input.Data).Transform(input.Data);
+            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidChangePointDetector", options);
+            var view = new IidChangePointEstimator(h, options).Fit(options.Data).Transform(options.Data);
             return new CommonOutputs.TransformOutput()
             {
-                Model = new TransformModelImpl(h, view, input.Data),
+                Model = new TransformModelImpl(h, view, options.Data),
                 OutputData = view
             };
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.IidSpikeDetector.Summary, UserName = TimeSeriesProcessing.IidSpikeDetector.UserName, ShortName = TimeSeriesProcessing.IidSpikeDetector.ShortName)]
-        public static CommonOutputs.TransformOutput IidSpikeDetector(IHostEnvironment env, IidSpikeDetector.Arguments input)
+        internal static CommonOutputs.TransformOutput IidSpikeDetector(IHostEnvironment env, IidSpikeDetector.Options options)
         {
-            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidSpikeDetector", input);
-            var view = new IidSpikeEstimator(h, input).Fit(input.Data).Transform(input.Data);
+            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidSpikeDetector", options);
+            var view = new IidSpikeEstimator(h, options).Fit(options.Data).Transform(options.Data);
             return new CommonOutputs.TransformOutput()
             {
-                Model = new TransformModelImpl(h, view, input.Data),
+                Model = new TransformModelImpl(h, view, options.Data),
                 OutputData = view
             };
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.PercentileThresholdTransform.Summary, UserName = TimeSeriesProcessing.PercentileThresholdTransform.UserName, ShortName = TimeSeriesProcessing.PercentileThresholdTransform.ShortName)]
-        public static CommonOutputs.TransformOutput PercentileThresholdTransform(IHostEnvironment env, PercentileThresholdTransform.Arguments input)
+        internal static CommonOutputs.TransformOutput PercentileThresholdTransform(IHostEnvironment env, PercentileThresholdTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "PercentileThresholdTransform", input);
             var view = new PercentileThresholdTransform(h, input, input.Data);
@@ -63,7 +63,7 @@ public static CommonOutputs.TransformOutput PercentileThresholdTransform(IHostEn
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.PValueTransform.Summary, UserName = TimeSeriesProcessing.PValueTransform.UserName, ShortName = TimeSeriesProcessing.PValueTransform.ShortName)]
-        public static CommonOutputs.TransformOutput PValueTransform(IHostEnvironment env, PValueTransform.Arguments input)
+        internal static CommonOutputs.TransformOutput PValueTransform(IHostEnvironment env, PValueTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "PValueTransform", input);
             var view = new PValueTransform(h, input, input.Data);
@@ -75,7 +75,7 @@ public static CommonOutputs.TransformOutput PValueTransform(IHostEnvironment env
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SlidingWindowTransform.Summary, UserName = TimeSeriesProcessing.SlidingWindowTransform.UserName, ShortName = TimeSeriesProcessing.SlidingWindowTransform.ShortName)]
-        public static CommonOutputs.TransformOutput SlidingWindowTransform(IHostEnvironment env, SlidingWindowTransform.Arguments input)
+        internal static CommonOutputs.TransformOutput SlidingWindowTransform(IHostEnvironment env, SlidingWindowTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SlidingWindowTransform", input);
             var view = new SlidingWindowTransform(h, input, input.Data);
@@ -87,25 +87,25 @@ public static CommonOutputs.TransformOutput SlidingWindowTransform(IHostEnvironm
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SsaChangePointDetector.Summary, UserName = TimeSeriesProcessing.SsaChangePointDetector.UserName, ShortName = TimeSeriesProcessing.SsaChangePointDetector.ShortName)]
-        public static CommonOutputs.TransformOutput SsaChangePointDetector(IHostEnvironment env, SsaChangePointDetector.Arguments input)
+        internal static CommonOutputs.TransformOutput SsaChangePointDetector(IHostEnvironment env, SsaChangePointDetector.Options options)
         {
-            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaChangePointDetector", input);
-            var view = new SsaChangePointEstimator(h, input).Fit(input.Data).Transform(input.Data);
+            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaChangePointDetector", options);
+            var view = new SsaChangePointEstimator(h, options).Fit(options.Data).Transform(options.Data);
             return new CommonOutputs.TransformOutput()
             {
-                Model = new TransformModelImpl(h, view, input.Data),
+                Model = new TransformModelImpl(h, view, options.Data),
                 OutputData = view
             };
         }
 
         [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SsaSpikeDetector.Summary, UserName = TimeSeriesProcessing.SsaSpikeDetector.UserName, ShortName = TimeSeriesProcessing.SsaSpikeDetector.ShortName)]
-        public static CommonOutputs.TransformOutput SsaSpikeDetector(IHostEnvironment env, SsaSpikeDetector.Arguments input)
+        internal static CommonOutputs.TransformOutput SsaSpikeDetector(IHostEnvironment env, SsaSpikeDetector.Options options)
         {
-            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaSpikeDetector", input);
-            var view = new SsaSpikeEstimator(h, input).Fit(input.Data).Transform(input.Data);
+            var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaSpikeDetector", options);
+            var view = new SsaSpikeEstimator(h, options).Fit(options.Data).Transform(options.Data);
             return new CommonOutputs.TransformOutput()
             {
-                Model = new TransformModelImpl(h, view, input.Data),
+                Model = new TransformModelImpl(h, view, options.Data),
                 OutputData = view
             };
         }
diff --git a/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs b/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
index 537b0f7307..b8153cd03f 100644
--- a/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
+++ b/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
@@ -27,7 +27,7 @@ namespace Microsoft.ML.TimeSeriesProcessing
     /// This class does not explicitly store the trajectory matrix though. Furthermore, since the trajectory matrix is
     /// a Hankel matrix, its multiplication by an arbitrary vector is implemented efficiently using the Discrete Fast Fourier Transform.
     /// </summary>
-    public sealed class TrajectoryMatrix
+    internal sealed class TrajectoryMatrix
     {
         /// <summary>
         /// The time series data
diff --git a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
index 256d946834..d1a8e3c139 100644
--- a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
+++ b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
@@ -32,13 +32,13 @@ Models.RegressionPipelineEnsemble	Combine regression models into an ensemble	Mic
 Models.Summarizer	Summarize a linear regression predictor.	Microsoft.ML.EntryPoints.SummarizePredictor	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.EntryPoints.CommonOutputs+SummaryOutput
 Models.TrainTestEvaluator	General train test for any supported evaluator	Microsoft.ML.EntryPoints.TrainTestMacro	TrainTest	Microsoft.ML.EntryPoints.TrainTestMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.TrainTestMacro+Output]
 TimeSeriesProcessingEntryPoints.ExponentialAverage	Applies a Exponential average on a time series.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	ExponentialAverage	Microsoft.ML.TimeSeriesProcessing.ExponentialAverageTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.IidChangePointDetector	This transform detects the change-points in an i.i.d. sequence using adaptive kernel density estimation and martingales.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidChangePointDetector	Microsoft.ML.TimeSeriesProcessing.IidChangePointDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.IidSpikeDetector	This transform detects the spikes in a i.i.d. sequence using adaptive kernel density estimation.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidSpikeDetector	Microsoft.ML.TimeSeriesProcessing.IidSpikeDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.IidChangePointDetector	This transform detects the change-points in an i.i.d. sequence using adaptive kernel density estimation and martingales.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidChangePointDetector	Microsoft.ML.TimeSeriesProcessing.IidChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.IidSpikeDetector	This transform detects the spikes in a i.i.d. sequence using adaptive kernel density estimation.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidSpikeDetector	Microsoft.ML.TimeSeriesProcessing.IidSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 TimeSeriesProcessingEntryPoints.PercentileThresholdTransform	Detects the values of time-series that are in the top percentile of the sliding window.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	PercentileThresholdTransform	Microsoft.ML.TimeSeriesProcessing.PercentileThresholdTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 TimeSeriesProcessingEntryPoints.PValueTransform	This P-Value transform calculates the p-value of the current input in the sequence with regard to the values in the sliding window.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	PValueTransform	Microsoft.ML.TimeSeriesProcessing.PValueTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 TimeSeriesProcessingEntryPoints.SlidingWindowTransform	Returns the last values for a time series [y(t-d-l+1), y(t-d-l+2), ..., y(t-l-1), y(t-l)] where d is the size of the window, l the lag and y is a Float.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SlidingWindowTransform	Microsoft.ML.TimeSeriesProcessing.SlidingWindowTransformBase`1+Arguments[System.Single]	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.SsaChangePointDetector	This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaChangePointDetector	Microsoft.ML.TimeSeriesProcessing.SsaChangePointDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.SsaSpikeDetector	This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaSpikeDetector	Microsoft.ML.TimeSeriesProcessing.SsaSpikeDetector+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.SsaChangePointDetector	This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaChangePointDetector	Microsoft.ML.TimeSeriesProcessing.SsaChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.SsaSpikeDetector	This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaSpikeDetector	Microsoft.ML.TimeSeriesProcessing.SsaSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 Trainers.AveragedPerceptronBinaryClassifier	Averaged Perceptron Binary Classifier.	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer	TrainBinary	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Trainers.EnsembleBinaryClassifier	Train binary ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Trainers.EnsembleClassification	Train multiclass ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateMultiClassEnsemble	Microsoft.ML.Ensemble.MulticlassDataPartitionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
index 93dc3f8a48..5f9863b5d1 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
@@ -56,7 +56,7 @@ public void ChangeDetection()
             for (int i = 0; i < size / 2; i++)
                 data.Add(new Data((float)(5 + i * 1.1)));
 
-            var args = new IidChangePointDetector.Arguments()
+            var args = new IidChangePointDetector.Options()
             {
                 Confidence = 80,
                 Source = "Value",
@@ -96,7 +96,7 @@ public void ChangePointDetectionWithSeasonality()
             List<Data> data = new List<Data>();
             var dataView = env.Data.ReadFromEnumerable(data);
 
-            var args = new SsaChangePointDetector.Arguments()
+            var args = new SsaChangePointDetector.Options()
             {
                 Confidence = 95,
                 Source = "Value",
@@ -157,7 +157,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngineNoColumn()
 
             // Pipeline.
             var pipeline = ml.Transforms.Text.FeaturizeText("Text_Featurized", "Text")
-                .Append(new SsaChangePointEstimator(ml, new SsaChangePointDetector.Arguments()
+                .Append(new SsaChangePointEstimator(ml, new SsaChangePointDetector.Options()
                 {
                     Confidence = 95,
                     Source = "Value",
@@ -233,7 +233,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngine()
 
             // Pipeline.
             var pipeline = ml.Transforms.Text.FeaturizeText("Text_Featurized", "Text")
-                .Append(new SsaChangePointEstimator(ml, new SsaChangePointDetector.Arguments()
+                .Append(new SsaChangePointEstimator(ml, new SsaChangePointDetector.Options()
                 {
                     Confidence = 95,
                     Source = "Value",

From b863ac2458f486834a9da3ecdd5a69f51f99db73 Mon Sep 17 00:00:00 2001
From: Jon Wood <jwood803@users.noreply.github.com>
Date: Mon, 11 Feb 2019 12:19:54 -0500
Subject: [PATCH 11/23] Update cookbook (#2494)

* Switch OneHotEncoding parameters

* Fix table of contents

* Update for spacing
---
 docs/code/MlNetCookBook.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/code/MlNetCookBook.md b/docs/code/MlNetCookBook.md
index 1a4b423e82..1551e275d3 100644
--- a/docs/code/MlNetCookBook.md
+++ b/docs/code/MlNetCookBook.md
@@ -688,11 +688,11 @@ var catColumns = data.GetColumn<string[]>(mlContext, "CategoricalFeatures").Take
 // Build several alternative featurization pipelines.
 var pipeline =
     // Convert each categorical feature into one-hot encoding independently.
-    mlContext.Transforms.Categorical.OneHotEncoding("CategoricalFeatures", "CategoricalOneHot")
+    mlContext.Transforms.Categorical.OneHotEncoding("CategoricalOneHot", "CategoricalFeatures")
     // Convert all categorical features into indices, and build a 'word bag' of these.
-    .Append(mlContext.Transforms.Categorical.OneHotEncoding("CategoricalFeatures", "CategoricalBag", CategoricalTransform.OutputKind.Bag))
+    .Append(mlContext.Transforms.Categorical.OneHotEncoding("CategoricalBag", "CategoricalFeatures", CategoricalTransform.OutputKind.Bag))
     // One-hot encode the workclass column, then drop all the categories that have fewer than 10 instances in the train set.
-    .Append(mlContext.Transforms.Categorical.OneHotEncoding("Workclass", "WorkclassOneHot"))
+    .Append(mlContext.Transforms.Categorical.OneHotEncoding("WorkclassOneHot", "Workclass"))
     .Append(mlContext.Transforms.FeatureSelection.CountFeatureSelectingEstimator("WorkclassOneHot", "WorkclassOneHotTrimmed", count: 10));
 
 // Let's train our pipeline, and then apply it to the same data.

From 8444a5a2a4c6b162c35b98cbff95983db3d676c2 Mon Sep 17 00:00:00 2001
From: Marek Linka <mareklinka@users.noreply.github.com>
Date: Mon, 11 Feb 2019 21:47:22 +0100
Subject: [PATCH 12/23] Replace ConditionalFact usages with custom facts
 (#2402)

* Replace ConditionalFact usages with custom facts

* Refactor ConditionalTheory usages

Add AttributeUsage attributes

* Make LessThanNetCore30OrNotNetCoreFactAttribute require an explicit skip message

* Use correct target framework moniker for netcoreapp3.0

* Remove CORECLR constant

* Remove an unused fact attribute
---
 .../BenchmarksTest.cs                         | 13 +---
 test/Microsoft.ML.Benchmarks/README.md        |  2 +-
 .../UnitTests/TestEntryPoints.cs              | 13 ++--
 .../DnnImageFeaturizerTest.cs                 | 22 ++-----
 .../OnnxTransformTests.cs                     | 27 +-------
 .../TestPredictors.cs                         | 65 +++++++++----------
 .../Training.cs                               | 13 ++--
 .../Attributes/BenchmarkTheoryAttribute.cs    | 29 +++++++++
 .../EnvironmentSpecificFactAttribute.cs       | 34 ++++++++++
 .../EnvironmentSpecificTheoryAttribute.cs     | 34 ++++++++++
 ...etCore30OrNotNetCoreAndX64FactAttribute.cs | 24 +++++++
 ...sThanNetCore30OrNotNetCoreFactAttribute.cs | 24 +++++++
 .../Attributes/LightGBMFactAttribute.cs       | 24 +++++++
 .../MatrixFactorizationFactAttribute.cs       | 24 +++++++
 .../Attributes/OnnxFactAttribute.cs           | 25 +++++++
 .../Attributes/OnnxTheoryAttribute.cs         | 25 +++++++
 .../Attributes/TensorflowFactAttribute.cs     | 24 +++++++
 .../Attributes/X64FactAttribute.cs            | 24 +++++++
 .../BaseTestBaseline.cs                       | 25 +------
 .../DataPipe/TestDataPipeBase.cs              | 48 --------------
 .../Microsoft.ML.TestFramework.csproj         |  4 --
 .../TestCommandBase.cs                        |  5 +-
 .../FeatureContributionTests.cs               |  7 +-
 test/Microsoft.ML.Tests/OnnxConversionTest.cs |  5 +-
 .../Scenarios/TensorflowTests.cs              |  5 +-
 .../TensorflowTests.cs                        | 25 +++----
 .../TensorFlowEstimatorTests.cs               | 11 ++--
 .../MatrixFactorizationTests.cs               | 15 +++--
 .../TrainerEstimators/TreeEstimators.cs       | 15 +++--
 .../Transformers/NormalizerTests.cs           |  3 +-
 .../TimeSeries.cs                             |  3 +-
 .../TimeSeriesDirectApi.cs                    |  7 +-
 32 files changed, 403 insertions(+), 221 deletions(-)
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/BenchmarkTheoryAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificTheoryAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreAndX64FactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/LightGBMFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/MatrixFactorizationFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/OnnxFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/OnnxTheoryAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/TensorflowFactAttribute.cs
 create mode 100644 test/Microsoft.ML.TestFramework/Attributes/X64FactAttribute.cs

diff --git a/test/Microsoft.ML.Benchmarks.Tests/BenchmarksTest.cs b/test/Microsoft.ML.Benchmarks.Tests/BenchmarksTest.cs
index 940ca83dd6..9dc6854e8b 100644
--- a/test/Microsoft.ML.Benchmarks.Tests/BenchmarksTest.cs
+++ b/test/Microsoft.ML.Benchmarks.Tests/BenchmarksTest.cs
@@ -12,7 +12,7 @@
 using BenchmarkDotNet.Loggers;
 using BenchmarkDotNet.Running;
 using Microsoft.ML.Benchmarks.Harness;
-using Microsoft.ML.Internal.CpuMath;
+using Microsoft.ML.TestFramework.Attributes;
 using Xunit;
 using Xunit.Abstractions;
 
@@ -29,15 +29,6 @@ public class BenchmarksTest
 
         private ITestOutputHelper Output { get; }
 
-        public static bool CanExecute =>
-#if DEBUG
-            false; // BenchmarkDotNet does not allow running the benchmarks in Debug, so this test is disabled for DEBUG
-#elif NET461
-            false; // We are currently not running Benchmarks for FullFramework
-#else
-            Environment.Is64BitProcess; // we don't support 32 bit yet
-#endif
-
         public static TheoryData<Type> GetBenchmarks()
         {
             TheoryData<Type> benchmarks = new TheoryData<Type>();
@@ -54,7 +45,7 @@ where Attribute.IsDefined(type, typeof(CIBenchmark))
             return benchmarks;
         }
 
-        [ConditionalTheory(typeof(BenchmarksTest), nameof(CanExecute))]
+        [BenchmarkTheory]
         [MemberData(nameof(GetBenchmarks))]
         public void BenchmarksProjectIsNotBroken(Type type)
         {
diff --git a/test/Microsoft.ML.Benchmarks/README.md b/test/Microsoft.ML.Benchmarks/README.md
index 4cefdfbaab..0f84b4ca46 100644
--- a/test/Microsoft.ML.Benchmarks/README.md
+++ b/test/Microsoft.ML.Benchmarks/README.md
@@ -94,7 +94,7 @@ you can debug this test locally by:
 build.cmd -release -buildNative
 
 2- Changing the configuration in Visual Studio from Debug -> Release
-3- Changing the annotation in the `BenchmarksProjectIsNotBroken` to replace `ConditionalTheory` with `Theory`, as below. 
+3- Changing the annotation in the `BenchmarksProjectIsNotBroken` to replace `BenchmarkTheory` with `Theory`, as below. 
 
 ```cs
 [Theory]
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
index c9ae3f9da3..4f5686c3d7 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
@@ -22,6 +22,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model.Onnx;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.TimeSeriesProcessing;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
@@ -1312,7 +1313,7 @@ public void EntryPointMulticlassPipelineEnsemble()
             }
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void EntryPointPipelineEnsembleGetSummary()
         {
             var dataPath = GetDataPath("breast-cancer-withheader.txt");
@@ -1916,14 +1917,14 @@ public void EntryPointEvaluateRanking()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void EntryPointLightGbmBinary()
         {
             Env.ComponentCatalog.RegisterAssembly(typeof(LightGbmBinaryModelParameters).Assembly);
             TestEntryPointRoutine("breast-cancer.txt", "Trainers.LightGbmBinaryClassifier");
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void EntryPointLightGbmMultiClass()
         {
             Env.ComponentCatalog.RegisterAssembly(typeof(LightGbmBinaryModelParameters).Assembly);
@@ -3649,7 +3650,7 @@ public void EntryPointWordEmbeddings()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void EntryPointTensorFlowTransform()
         {
             Env.ComponentCatalog.RegisterAssembly(typeof(TensorFlowTransformer).Assembly);
@@ -4069,7 +4070,7 @@ public void TestSimpleTrainExperiment()
             }
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void TestCrossValidationMacro()
         {
             var dataPath = GetDataPath(TestDatasets.generatedRegressionDatasetmacro.trainFilename);
@@ -5537,7 +5538,7 @@ public void TestOvaMacroWithUncalibratedLearner()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TestTensorFlowEntryPoint()
         {
             var dataPath = GetDataPath("Train-Tiny-28x28.txt");
diff --git a/test/Microsoft.ML.OnnxTransformTest/DnnImageFeaturizerTest.cs b/test/Microsoft.ML.OnnxTransformTest/DnnImageFeaturizerTest.cs
index 70ec3dffc8..2ebb55ee4d 100644
--- a/test/Microsoft.ML.OnnxTransformTest/DnnImageFeaturizerTest.cs
+++ b/test/Microsoft.ML.OnnxTransformTest/DnnImageFeaturizerTest.cs
@@ -13,6 +13,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.StaticPipe;
 using Xunit;
@@ -57,16 +58,9 @@ public DnnImageFeaturizerTests(ITestOutputHelper helper) : base(helper)
         {
         }
 
-        // Onnx is only supported on x64 Windows
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [OnnxFact]
         void TestDnnImageFeaturizer()
         {
-            // Onnxruntime supports Ubuntu 16.04, but not CentOS
-            // Do not execute on CentOS image
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                return;
-
-
             var samplevector = GetSampleArrayData();
 
             var dataView = DataViewConstructionUtils.CreateFromList(Env,
@@ -97,13 +91,9 @@ void TestDnnImageFeaturizer()
             catch (InvalidOperationException) { }
         }
 
-        // Onnx is only supported on x64 Windows
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [OnnxFact]
         public void OnnxStatic()
         {
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                return;
-
             var env = new MLContext(null, 1);
             var imageHeight = 224;
             var imageWidth = 224;
@@ -141,13 +131,9 @@ public void OnnxStatic()
         }
 
         // Onnx is only supported on x64 Windows
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [OnnxFact]
         public void TestOldSavingAndLoading()
         {
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                return;
-
-
             var samplevector = GetSampleArrayData();
 
             var dataView = ML.Data.ReadFromEnumerable(
diff --git a/test/Microsoft.ML.OnnxTransformTest/OnnxTransformTests.cs b/test/Microsoft.ML.OnnxTransformTest/OnnxTransformTests.cs
index 904282a7a2..6b95e217db 100644
--- a/test/Microsoft.ML.OnnxTransformTest/OnnxTransformTests.cs
+++ b/test/Microsoft.ML.OnnxTransformTest/OnnxTransformTests.cs
@@ -13,6 +13,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Tools;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.StaticPipe;
@@ -21,24 +22,6 @@
 
 namespace Microsoft.ML.Tests
 {
-
-    /// <summary>
-    /// A Fact attribute for Onnx unit tests. Onnxruntime only supported
-    /// on Windows, Linux (Ubuntu 16.04) and 64-bit platforms.
-    /// </summary>
-    public class OnnxFact : FactAttribute
-    {
-        public OnnxFact()
-        {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
-                RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ||
-                !Environment.Is64BitProcess)
-            {
-                Skip = "Require 64 bit and Windows or Linux (Ubuntu 16.04).";
-            }
-        }
-    }
-
     public class OnnxTransformTests : TestDataPipeBase
     {
         private const int inputSize = 150528;
@@ -137,16 +120,12 @@ void TestSimpleCase()
             catch (ArgumentOutOfRangeException) { }
             catch (InvalidOperationException) { }
         }
-
-        // x86 not supported
-        [ConditionalTheory(typeof(Environment), nameof(Environment.Is64BitProcess))]
+ 
+        [OnnxTheory]
         [InlineData(null, false)]
         [InlineData(null, true)]
         void TestOldSavingAndLoading(int? gpuDeviceId, bool fallbackToCpu)
         {
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                return;
-
             var modelFile = "squeezenet/00000001/model.onnx";
             var samplevector = GetSampleArrayData();
 
diff --git a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
index 4ecd744c36..0af5ad72b5 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using Microsoft.ML.TestFramework.Attributes;
 using Float = System.Single;
 
 namespace Microsoft.ML.RunTests
@@ -160,7 +161,7 @@ public void EarlyStoppingTest()
         /// <summary>
         /// Multiclass Logistic Regression test.
         /// </summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory("Multiclass")]
         [TestCategory("Logistic Regression")]
         public void MulticlassLRTest()
@@ -172,7 +173,7 @@ public void MulticlassLRTest()
         /// <summary>
         /// Multiclass Logistic Regression with non-negative coefficients test.
         /// </summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory("Multiclass")]
         [TestCategory("Logistic Regression")]
         public void MulticlassLRNonNegativeTest()
@@ -196,7 +197,7 @@ public void MulticlassSdcaTest()
         /// <summary>
         /// Multiclass Logistic Regression test with a tree featurizer.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Multiclass")]
         [TestCategory("Logistic Regression")]
         [TestCategory("FastTree")]
@@ -254,7 +255,7 @@ public void KMeansClusteringTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("SDCA")]
         public void LinearClassifierTest()
@@ -275,7 +276,7 @@ public void LinearClassifierTest()
         /// <summary>
         ///A test for binary classifiers
         ///</summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         public void BinaryClassifierLogisticRegressionTest()
         {
@@ -285,7 +286,7 @@ public void BinaryClassifierLogisticRegressionTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         public void BinaryClassifierSymSgdTest()
         {
@@ -297,7 +298,7 @@ public void BinaryClassifierSymSgdTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         public void BinaryClassifierTesterThresholdingTest()
         {
@@ -323,7 +324,7 @@ public void BinaryClassifierLogisticRegressionNormTest()
         /// <summary>
         ///A test for binary classifiers with non-negative coefficients
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCoreAnd64BitProcess))]  // netcore3.0 and x86 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreAndX64Fact("netcoreapp3.0 and x86 output differs from Baseline")]
         [TestCategory("Binary")]
         public void BinaryClassifierLogisticRegressionNonNegativeTest()
         {
@@ -336,7 +337,7 @@ public void BinaryClassifierLogisticRegressionNonNegativeTest()
         /// <summary>
         ///A test for binary classifiers
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]  // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory("Binary")]
         public void BinaryClassifierLogisticRegressionBinNormTest()
         {
@@ -349,7 +350,7 @@ public void BinaryClassifierLogisticRegressionBinNormTest()
         /// <summary>
         ///A test for binary classifiers
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCoreAnd64BitProcess))] // x86 output differs from Baseline and flaky on netcore 3.0
+        [LessThanNetCore30OrNotNetCoreAndX64Fact("x86 output differs from Baseline and flaky on netcore 3.0")]
         [TestCategory("Binary")]
         public void BinaryClassifierLogisticRegressionGaussianNormTest()
         {
@@ -386,7 +387,7 @@ public void BinaryClassifierFastRankClassificationTest()
         /// <summary>
         ///A test for binary classifiers
         ///</summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("FastForest")]
         public void FastForestClassificationTest()
@@ -453,7 +454,7 @@ public void WeightingFastForestRegressionPredictorsTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("FastTree")]
         public void FastTreeBinaryClassificationTest()
@@ -472,7 +473,7 @@ public void FastTreeBinaryClassificationTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Binary")]
         [TestCategory("LightGBM")]
         public void LightGBMClassificationTest()
@@ -488,7 +489,7 @@ public void LightGBMClassificationTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Binary"), TestCategory("LightGBM")]
         public void GossLightGBMTest()
         {
@@ -498,7 +499,7 @@ public void GossLightGBMTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Binary")]
         [TestCategory("LightGBM")]
         public void DartLightGBMTest()
@@ -512,7 +513,7 @@ public void DartLightGBMTest()
         /// <summary>
         /// A test for multi class classifiers.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Multiclass")]
         [TestCategory("LightGBM")]
         public void MultiClassifierLightGBMKeyLabelTest()
@@ -526,7 +527,7 @@ public void MultiClassifierLightGBMKeyLabelTest()
         /// <summary>
         /// A test for multi class classifiers.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Multiclass")]
         [TestCategory("LightGBM")]
         public void MultiClassifierLightGBMKeyLabelU404Test()
@@ -540,7 +541,7 @@ public void MultiClassifierLightGBMKeyLabelU404Test()
         /// <summary>
         /// A test for regression.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Regression")]
         [TestCategory("LightGBM")]
         public void RegressorLightGBMTest()
@@ -554,7 +555,7 @@ public void RegressorLightGBMTest()
         /// <summary>
         /// A test for regression.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Regression")]
         [TestCategory("LightGBM")]
         public void RegressorLightGBMMAETest()
@@ -568,7 +569,7 @@ public void RegressorLightGBMMAETest()
         /// <summary>
         /// A test for regression.
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         [TestCategory("Regression")]
         [TestCategory("LightGBM")]
         public void RegressorLightGBMRMSETest()
@@ -600,8 +601,7 @@ public void RankingLightGBMTest()
             Done();
         }
 
-        // x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [X64Fact("x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216")]
         public void TestTreeEnsembleCombiner()
         {
             var dataPath = GetDataPath("breast-cancer.txt");
@@ -622,8 +622,7 @@ public void TestTreeEnsembleCombiner()
             CombineAndTestTreeEnsembles(dataView, fastTrees);
         }
 
-        // x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [X64Fact("x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216")]
         public void TestTreeEnsembleCombinerWithCategoricalSplits()
         {
             var dataPath = GetDataPath("adult.tiny.with-schema.txt");
@@ -725,8 +724,7 @@ private void CombineAndTestTreeEnsembles(IDataView idv, PredictorModel[] fastTre
             }
         }
 
-        // x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [X64Fact("x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216")]
         public void TestEnsembleCombiner()
         {
             var dataPath = GetDataPath("breast-cancer.txt");
@@ -771,8 +769,7 @@ public void TestEnsembleCombiner()
             CombineAndTestEnsembles(dataView, "pe", "oc=average", PredictionKind.BinaryClassification, predictors);
         }
 
-        // x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [X64Fact("x86 fails. Associated GitHubIssue: https://github.com/dotnet/machinelearning/issues/1216")]
         public void TestMultiClassEnsembleCombiner()
         {
             var dataPath = GetDataPath("breast-cancer.txt");
@@ -941,7 +938,7 @@ private void CombineAndTestEnsembles(IDataView idv, string name, string options,
         }
 
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("FastTree")]
         public void FastTreeBinaryClassificationCategoricalSplitTest()
@@ -980,7 +977,7 @@ public void FastTreeRegressionCategoricalSplitTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("FastTree")]
         public void FastTreeBinaryClassificationNoOpGroupIdTest()
@@ -1000,7 +997,7 @@ public void FastTreeBinaryClassificationNoOpGroupIdTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         [TestCategory("Binary")]
         [TestCategory("FastTree")]
         public void FastTreeHighMinDocsTest()
@@ -1575,7 +1572,7 @@ public IList<TestDataset> GetDatasetsForCalibratorTest()
         /// <summary>
         ///A test for no calibrators
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]  // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory("Calibrator")]
         public void DefaultCalibratorPerceptronTest()
         {
@@ -1587,7 +1584,7 @@ public void DefaultCalibratorPerceptronTest()
         /// <summary>
         ///A test for PAV calibrators
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]  // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory("Calibrator")]
         public void PAVCalibratorPerceptronTest()
         {
@@ -1599,7 +1596,7 @@ public void PAVCalibratorPerceptronTest()
         /// <summary>
         ///A test for random calibrators
         ///</summary>
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCoreAnd64BitProcess))]  // netcore3.0 and x86 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreAndX64Fact("netcoreapp3.0 and x86 output differs from Baseline")]
         [TestCategory("Calibrator")]
         public void RandomCalibratorPerceptronTest()
         {
diff --git a/test/Microsoft.ML.StaticPipelineTesting/Training.cs b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
index c14b5071d2..5f76129a89 100644
--- a/test/Microsoft.ML.StaticPipelineTesting/Training.cs
+++ b/test/Microsoft.ML.StaticPipelineTesting/Training.cs
@@ -14,6 +14,7 @@
 using Microsoft.ML.LightGBM.StaticPipe;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Trainers.KMeans;
@@ -448,7 +449,7 @@ public void FastTreeRegression()
             Assert.InRange(metrics.LossFn, 0, double.PositiveInfinity);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmBinaryClassification()
         {
             var env = new MLContext(seed: 0);
@@ -488,7 +489,7 @@ public void LightGbmBinaryClassification()
             Assert.InRange(metrics.Auprc, 0, 1);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmRegression()
         {
             var env = new MLContext(seed: 0);
@@ -804,7 +805,7 @@ public void FastTreeRanking()
             Assert.InRange(metrics.Ndcg[2], 36.5, 37);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGBMRanking()
         {
             var env = new MLContext(seed: 0);
@@ -845,7 +846,7 @@ public void LightGBMRanking()
             Assert.InRange(metrics.Ndcg[2], 36.5, 37);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void MultiClassLightGBM()
         {
             var env = new MLContext(seed: 0);
@@ -966,7 +967,7 @@ public void HogwildSGDBinaryClassification()
             Assert.InRange(metrics.Auprc, 0, 1);
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCoreAnd64BitProcess))] // netcore3.0 and x86 output differs from Baseline. This test is being fixed as part of issue #1441.
+        [LessThanNetCore30OrNotNetCoreAndX64Fact("netcoreapp3.0 and x86 output differs from Baseline. Being tracked as part of https://github.com/dotnet/machinelearning/issues/1441")]
         public void MatrixFactorization()
         {
             // Create a new context for ML.NET operations. It can be used for exception tracking and logging,
@@ -1017,7 +1018,7 @@ public void MatrixFactorization()
             Assert.InRange(metrics.L2, 0, 0.5);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void MultiClassLightGbmStaticPipelineWithInMemoryData()
         {
             // Create a general context for ML.NET operations. It can be used for exception tracking and logging,
diff --git a/test/Microsoft.ML.TestFramework/Attributes/BenchmarkTheoryAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/BenchmarkTheoryAttribute.cs
new file mode 100644
index 0000000000..fae467176e
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/BenchmarkTheoryAttribute.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A theory for BenchmarkDotNet tests.
+    /// </summary>
+    public sealed class BenchmarkTheoryAttribute : EnvironmentSpecificTheoryAttribute
+    {
+#if DEBUG
+        private const string SkipMessage = "BenchmarkDotNet does not allow running the benchmarks in Debug, so this test is disabled for DEBUG";
+        private readonly bool _isEnvironmentSupported = false;
+#elif NET461
+        private const string SkipMessage = "We are currently not running Benchmarks for FullFramework";
+        private readonly bool _isEnvironmentSupported = false;
+#else
+        private const string SkipMessage = "We don't support 32 bit yet";
+        private readonly bool _isEnvironmentSupported = System.Environment.Is64BitProcess;
+#endif
+
+        public BenchmarkTheoryAttribute() : base(SkipMessage)
+        {
+        }
+
+        protected override bool IsEnvironmentSupported() => _isEnvironmentSupported;
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificFactAttribute.cs
new file mode 100644
index 0000000000..fe754b6eb2
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificFactAttribute.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Xunit;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A base class for environment-specific fact attributes.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+    public abstract class EnvironmentSpecificFactAttribute : FactAttribute
+    {
+        private readonly string _skipMessage;
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="EnvironmentSpecificFactAttribute" /> class.
+        /// </summary>
+        /// <param name="skipMessage">The message to be used when skipping the test marked with this attribute.</param>
+        protected EnvironmentSpecificFactAttribute(string skipMessage)
+        {
+            _skipMessage = skipMessage ?? throw new ArgumentNullException(nameof(skipMessage));
+        }
+
+        public sealed override string Skip => IsEnvironmentSupported() ? null : _skipMessage;
+
+        /// <summary>
+        /// A method used to evaluate whether to skip a test marked with this attribute. Skips iff this method evaluates to false.
+        /// </summary>
+        protected abstract bool IsEnvironmentSupported();
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificTheoryAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificTheoryAttribute.cs
new file mode 100644
index 0000000000..c8f4b44c56
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/EnvironmentSpecificTheoryAttribute.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Xunit;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A base class for environment-specific fact attributes.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+    public abstract class EnvironmentSpecificTheoryAttribute : TheoryAttribute
+    {
+        private readonly string _skipMessage;
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="EnvironmentSpecificTheoryAttribute" /> class.
+        /// </summary>
+        /// <param name="skipMessage">The message to be used when skipping the test marked with this attribute.</param>
+        protected EnvironmentSpecificTheoryAttribute(string skipMessage)
+        {
+            _skipMessage = skipMessage ?? throw new ArgumentNullException(nameof(skipMessage));
+        }
+
+        public sealed override string Skip => IsEnvironmentSupported() ? null : _skipMessage;
+
+        /// <summary>
+        /// A method used to evaluate whether to skip a test marked with this attribute. Skips iff this method evaluates to false.
+        /// </summary>
+        protected abstract bool IsEnvironmentSupported();
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreAndX64FactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreAndX64FactAttribute.cs
new file mode 100644
index 0000000000..4d8814b5d6
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreAndX64FactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring x64 environment and either .NET Core version lower than 3.0 or framework other than .NET Core.
+    /// </summary>
+    public sealed class LessThanNetCore30OrNotNetCoreAndX64FactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public LessThanNetCore30OrNotNetCoreAndX64FactAttribute(string skipMessage) : base(skipMessage)
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess && AppDomain.CurrentDomain.GetData("FX_PRODUCT_VERSION") == null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreFactAttribute.cs
new file mode 100644
index 0000000000..7fdfdf2708
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/LessThanNetCore30OrNotNetCoreFactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring either .NET Core version lower than 3.0 or framework other than .NET Core.
+    /// </summary>
+    public sealed class LessThanNetCore30OrNotNetCoreFactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public LessThanNetCore30OrNotNetCoreFactAttribute(string skipMessage) : base(skipMessage)
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return AppDomain.CurrentDomain.GetData("FX_PRODUCT_VERSION") == null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/LightGBMFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/LightGBMFactAttribute.cs
new file mode 100644
index 0000000000..726d50e970
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/LightGBMFactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring LightGBM.
+    /// </summary>
+    public sealed class LightGBMFactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public LightGBMFactAttribute() : base("LightGBM is 64-bit only")
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/MatrixFactorizationFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/MatrixFactorizationFactAttribute.cs
new file mode 100644
index 0000000000..8ba0bba3e1
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/MatrixFactorizationFactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring matrix factorization.
+    /// </summary>
+    public sealed class MatrixFactorizationFactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public MatrixFactorizationFactAttribute() : base("Disabled - this test is being fixed as part of https://github.com/dotnet/machinelearning/issues/1441")
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/OnnxFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/OnnxFactAttribute.cs
new file mode 100644
index 0000000000..d2ed6763d6
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/OnnxFactAttribute.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring Onnx.
+    /// </summary>
+    public sealed class OnnxFactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public OnnxFactAttribute() : base("Onnx is 64-bit Windows only")
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/OnnxTheoryAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/OnnxTheoryAttribute.cs
new file mode 100644
index 0000000000..979654c9c3
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/OnnxTheoryAttribute.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring Onnx.
+    /// </summary>
+    public sealed class OnnxTheoryAttribute : EnvironmentSpecificTheoryAttribute
+    {
+        public OnnxTheoryAttribute() : base("Onnx is 64-bit Windows only")
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/Attributes/TensorflowFactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/TensorflowFactAttribute.cs
new file mode 100644
index 0000000000..7711e4d05f
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/TensorflowFactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring TensorFlow.
+    /// </summary>
+    public sealed class TensorFlowFactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public TensorFlowFactAttribute() : base("TensorFlow is 64-bit only")
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess;
+        }
+    }
+}
diff --git a/test/Microsoft.ML.TestFramework/Attributes/X64FactAttribute.cs b/test/Microsoft.ML.TestFramework/Attributes/X64FactAttribute.cs
new file mode 100644
index 0000000000..dcf8814bb8
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/Attributes/X64FactAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.ML.TestFramework.Attributes
+{
+    /// <summary>
+    /// A fact for tests requiring X64 environment.
+    /// </summary>
+    public sealed class X64FactAttribute : EnvironmentSpecificFactAttribute
+    {
+        public X64FactAttribute(string skipMessage) : base(skipMessage)
+        {
+        }
+
+        /// <inheritdoc />
+        protected override bool IsEnvironmentSupported()
+        {
+            return Environment.Is64BitProcess;
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
index b3c2faf3be..43d89d9982 100644
--- a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
+++ b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
@@ -24,20 +24,8 @@ public abstract partial class BaseTestBaseline : BaseTestClass
     {
         public const int DigitsOfPrecision = 7;
 
-        public static bool NotFullFramework { get; set;} = true;
-
-        public static bool LessThanNetCore30OrNotNetCore { get; } = AppDomain.CurrentDomain.GetData("FX_PRODUCT_VERSION") == null ? true : false;
-
-        public static bool LessThanNetCore30AndNotFullFramework { get; set; } = LessThanNetCore30OrNotNetCore;
-
-        public static bool LessThanNetCore30OrNotNetCoreAnd64BitProcess { get; } = LessThanNetCore30OrNotNetCore && Environment.Is64BitProcess;
-
         protected BaseTestBaseline(ITestOutputHelper output) : base(output)
         {
-#if NETFRAMEWORK
-            NotFullFramework = false;
-            LessThanNetCore30AndNotFullFramework = false;
-#endif
         }
 
         internal const string RawSuffix = ".raw";
@@ -777,9 +765,6 @@ public void RunMTAThread(ThreadStart fn)
                 }
             });
             t.IsBackground = true;
-#if !CORECLR // CoreCLR does not support apartment state settings for threads.
-            t.SetApartmentState(ApartmentState.MTA);
-#endif
             t.Start();
             t.Join();
             if (inner != null)
@@ -802,11 +787,8 @@ public void RunMTAThread(ThreadStart fn)
         protected static StreamWriter OpenWriter(string path, bool append = false, Encoding encoding = null, int bufferSize = 1024)
         {
             Contracts.CheckNonWhiteSpace(path, nameof(path));
-#if CORECLR
+
             return Utils.OpenWriter(File.Open(path, append ? FileMode.Append : FileMode.OpenOrCreate), encoding, bufferSize, false);
-#else
-            return new StreamWriter(path, append);
-#endif
         }
 
         /// <summary>
@@ -817,11 +799,8 @@ protected static StreamWriter OpenWriter(string path, bool append = false, Encod
         protected static StreamReader OpenReader(string path)
         {
             Contracts.CheckNonWhiteSpace(path, nameof(path));
-#if CORECLR
+
             return new StreamReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read));
-#else
-            return new StreamReader(path);
-#endif
         }
 
         /// <summary>
diff --git a/test/Microsoft.ML.TestFramework/DataPipe/TestDataPipeBase.cs b/test/Microsoft.ML.TestFramework/DataPipe/TestDataPipeBase.cs
index 61149b7022..fa7c4c8ea0 100644
--- a/test/Microsoft.ML.TestFramework/DataPipe/TestDataPipeBase.cs
+++ b/test/Microsoft.ML.TestFramework/DataPipe/TestDataPipeBase.cs
@@ -1096,23 +1096,6 @@ protected Func<bool> GetColumnComparer(Row r1, Row r2, int col, ColumnType type,
                     return GetComparerVec<RowId>(r1, r2, col, size, (x, y) => x.Equals(y));
             }
 
-#if !CORECLR // REVIEW: Port Picture type to CoreTLC.
-            if (type is PictureType)
-            {
-                var g1 = r1.GetGetter<Picture>(col);
-                var g2 = r2.GetGetter<Picture>(col);
-                Picture v1 = null;
-                Picture v2 = null;
-                return
-                    () =>
-                    {
-                        g1(ref v1);
-                        g2(ref v2);
-                        return ComparePicture(v1, v2);
-                    };
-            }
-#endif
-
             throw Contracts.Except("Unknown type in GetColumnComparer: '{0}'", type);
         }
 
@@ -1250,36 +1233,5 @@ protected void VerifyVecEquality<T>(ValueGetter<VBuffer<T>> vecGetter, ValueGett
             vecNGetter(ref fvn);
             Assert.True(CompareVec(in fv, in fvn, size, compare));
         }
-
-#if !CORECLR
-        // REVIEW: Port Picture type to Core TLC.
-        protected bool ComparePicture(Picture v1, Picture v2)
-        {
-            if (v1 == null || v2 == null)
-                return v1 == v2;
-
-            var p1 = v1.Contents.Pixels;
-            var p2 = v2.Contents.Pixels;
-
-            if (p1.Width != p2.Width)
-                return false;
-            if (p1.Height != p2.Height)
-                return false;
-            if (p1.PixelFormat != p2.PixelFormat)
-                return false;
-
-            for (int y = 0; y < p1.Height; y++)
-            {
-                for (int x = 0; x < p1.Width; x++)
-                {
-                    var x1 = p1.GetPixel(x, y);
-                    var x2 = p2.GetPixel(x, y);
-                    if (x1 != x2)
-                        return false;
-                }
-            }
-            return true;
-        }
-#endif
     }
 }
diff --git a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
index a8a0ad7000..d24892a1a8 100644
--- a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
+++ b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
@@ -1,8 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <DefineConstants>CORECLR</DefineConstants>
-  </PropertyGroup>
-  
   <ItemGroup>
     <ProjectReference Include="..\..\src\Microsoft.ML.Core\Microsoft.ML.Core.csproj" />
     <ProjectReference Include="..\..\src\Microsoft.ML.Data\Microsoft.ML.Data.csproj" />
diff --git a/test/Microsoft.ML.TestFramework/TestCommandBase.cs b/test/Microsoft.ML.TestFramework/TestCommandBase.cs
index cb3a88782f..4475568a6b 100644
--- a/test/Microsoft.ML.TestFramework/TestCommandBase.cs
+++ b/test/Microsoft.ML.TestFramework/TestCommandBase.cs
@@ -15,6 +15,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.TestFramework;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Tools;
 using Xunit;
 using Xunit.Abstractions;
@@ -838,7 +839,7 @@ public void CommandCrossValidation()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [X64Fact("x86 output differs from Baseline")]
         public void CommandCrossValidationKeyLabelWithFloatKeyValues()
         {
             RunMTAThread(() =>
@@ -1170,7 +1171,7 @@ public void CommandTrainMlrWithLabelNames()
             Done();
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         [TestCategory(Cat), TestCategory("Multiclass"), TestCategory("Logistic Regression")]
         public void CommandTrainMlrWithStats()
         {
diff --git a/test/Microsoft.ML.Tests/FeatureContributionTests.cs b/test/Microsoft.ML.Tests/FeatureContributionTests.cs
index 446aed03a0..7833b5afae 100644
--- a/test/Microsoft.ML.Tests/FeatureContributionTests.cs
+++ b/test/Microsoft.ML.Tests/FeatureContributionTests.cs
@@ -10,6 +10,7 @@
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Training;
 using Microsoft.ML.Transforms;
@@ -47,7 +48,7 @@ public void TestOrdinaryLeastSquaresRegression()
             TestFeatureContribution(ML.Regression.Trainers.OrdinaryLeastSquares(), GetSparseDataset(numberOfInstances: 100), "LeastSquaresRegression");
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void TestLightGbmRegression()
         {
             TestFeatureContribution(ML.Regression.Trainers.LightGbm(), GetSparseDataset(numberOfInstances: 100), "LightGbmRegression");
@@ -104,7 +105,7 @@ public void TestFastTreeRanking()
             TestFeatureContribution(ML.Ranking.Trainers.FastTree(), GetSparseDataset(TaskType.Ranking, 100), "FastTreeRanking");
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void TestLightGbmRanking()
         {
             TestFeatureContribution(ML.Ranking.Trainers.LightGbm(), GetSparseDataset(TaskType.Ranking, 100), "LightGbmRanking");
@@ -141,7 +142,7 @@ public void TestFastTreeBinary()
             TestFeatureContribution(ML.BinaryClassification.Trainers.FastTree(), GetSparseDataset(TaskType.BinaryClassification, 100), "FastTreeBinary");
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void TestLightGbmBinary()
         {
             TestFeatureContribution(ML.BinaryClassification.Trainers.LightGbm(), GetSparseDataset(TaskType.BinaryClassification, 100), "LightGbmBinary");
diff --git a/test/Microsoft.ML.Tests/OnnxConversionTest.cs b/test/Microsoft.ML.Tests/OnnxConversionTest.cs
index 00b7435d91..4d8a06972f 100644
--- a/test/Microsoft.ML.Tests/OnnxConversionTest.cs
+++ b/test/Microsoft.ML.Tests/OnnxConversionTest.cs
@@ -13,6 +13,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Tools;
 using Microsoft.ML.Trainers;
 using Microsoft.ML.Transforms;
@@ -118,7 +119,7 @@ private class BreastCancerMulticlassExample
             public float[] Features;
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // Tracked by https://github.com/dotnet/machinelearning/issues/2087
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline. Tracked by https://github.com/dotnet/machinelearning/issues/2087")]
         public void KmeansOnnxConversionTest()
         {
             // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
@@ -330,7 +331,7 @@ public void LogisticRegressionOnnxConversionTest()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmBinaryClassificationOnnxConversionTest()
         {
             // Step 1: Create and train a ML.NET pipeline.
diff --git a/test/Microsoft.ML.Tests/Scenarios/TensorflowTests.cs b/test/Microsoft.ML.Tests/Scenarios/TensorflowTests.cs
index c36fb058a5..8d615d9330 100644
--- a/test/Microsoft.ML.Tests/Scenarios/TensorflowTests.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/TensorflowTests.cs
@@ -2,11 +2,10 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
 using System.IO;
 using Microsoft.ML.Data;
 using Microsoft.ML.ImageAnalytics;
-using Microsoft.ML.Trainers;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.Conversions;
 using Xunit;
@@ -15,7 +14,7 @@ namespace Microsoft.ML.Scenarios
 {
     public partial class ScenariosTests
     {
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransforCifarEndToEndTest()
         {
             var imageHeight = 32;
diff --git a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
index 398d426684..f006c5a7f0 100644
--- a/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
+++ b/test/Microsoft.ML.Tests/ScenariosWithDirectInstantiation/TensorflowTests.cs
@@ -10,6 +10,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.ImageAnalytics;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.Conversions;
 using Microsoft.ML.Transforms.Normalizers;
@@ -29,7 +30,7 @@ private class TestData
             public float[] b;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformMatrixMultiplicationTest()
         {
             var modelLocation = "model_matmul/frozen_saved_model.pb";
@@ -214,7 +215,7 @@ private class TypesData
         /// <summary>
         /// Test to ensure the supported datatypes can passed to TensorFlow .
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformInputOutputTypesTest()
         {
             // This an identity model which returns the same output as input.
@@ -408,7 +409,7 @@ public void TensorFlowTransformInceptionTest()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowInputsOutputsSchemaTest()
         {
             var mlContext = new MLContext(seed: 1, conc: 1);
@@ -485,7 +486,7 @@ public void TensorFlowInputsOutputsSchemaTest()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformMNISTConvTest()
         {
             var mlContext = new MLContext(seed: 1, conc: 1);
@@ -523,7 +524,7 @@ public void TensorFlowTransformMNISTConvTest()
             Assert.Equal(5, GetMaxIndexForOnePrediction(onePrediction));
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformMNISTLRTrainingTest()
         {
             const double expectedMicroAccuracy = 0.72173913043478266;
@@ -608,7 +609,7 @@ private void CleanUp(string model_location)
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformMNISTConvTrainingTest()
         {
             ExecuteTFTransformMNISTConvTrainingTest(false, null, 0.74782608695652175, 0.608843537414966);
@@ -703,7 +704,7 @@ private void ExecuteTFTransformMNISTConvTrainingTest(bool shuffle, int? shuffleS
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformMNISTConvSavedModelTest()
         {
             // This test trains a multi-class classifier pipeline where a pre-trained Tenroflow model is used for featurization.
@@ -826,7 +827,7 @@ public class MNISTPrediction
             public float[] PredictedLabels;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformCifar()
         {
             var modelLocation = "cifar_model/frozen_model.pb";
@@ -872,7 +873,7 @@ public void TensorFlowTransformCifar()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowTransformCifarSavedModel()
         {
             var modelLocation = "cifar_saved_model";
@@ -914,7 +915,7 @@ public void TensorFlowTransformCifarSavedModel()
         }
 
         // This test has been created as result of https://github.com/dotnet/machinelearning/issues/2156.
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TensorFlowGettingSchemaMultipleTimes()
         {
             var modelLocation = "cifar_saved_model";
@@ -927,7 +928,7 @@ public void TensorFlowGettingSchemaMultipleTimes()
         }
 
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [TensorFlowFact]
         public void TensorFlowTransformCifarInvalidShape()
         {
             var modelLocation = "cifar_model/frozen_model.pb";
@@ -972,7 +973,7 @@ public class TensorFlowSentiment
             public float[] Prediction;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [TensorFlowFact]
         public void TensorFlowSentimentClassificationTest()
         {
             var mlContext = new MLContext(seed: 1, conc: 1);
diff --git a/test/Microsoft.ML.Tests/TensorFlowEstimatorTests.cs b/test/Microsoft.ML.Tests/TensorFlowEstimatorTests.cs
index a7d75f86cb..9a05f95bbf 100644
--- a/test/Microsoft.ML.Tests/TensorFlowEstimatorTests.cs
+++ b/test/Microsoft.ML.Tests/TensorFlowEstimatorTests.cs
@@ -12,6 +12,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Tools;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.StaticPipe;
@@ -56,7 +57,7 @@ public TensorFlowEstimatorTests(ITestOutputHelper output) : base(output)
         {
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         void TestSimpleCase()
         {
             var modelFile = "model_matmul/frozen_saved_model.pb";
@@ -96,7 +97,7 @@ void TestSimpleCase()
             catch (InvalidOperationException) { }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         void TestOldSavingAndLoading()
         {
             var modelFile = "model_matmul/frozen_saved_model.pb";
@@ -132,7 +133,7 @@ void TestOldSavingAndLoading()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // x86 output differs from Baseline
+        [TensorFlowFact]
         void TestCommandLine()
         {
             // typeof helps to load the TensorFlowTransformer type.
@@ -140,7 +141,7 @@ void TestCommandLine()
             Assert.Equal(Maml.Main(new[] { @"showschema loader=Text{col=a:R4:0-3 col=b:R4:0-3} xf=TFTransform{inputs=a inputs=b outputs=c modellocation={model_matmul/frozen_saved_model.pb}}" }), (int)0);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // TensorFlow is 64-bit only
+        [TensorFlowFact]
         public void TestTensorFlowStatic()
         {
             var modelLocation = "cifar_model/frozen_model.pb";
@@ -182,7 +183,7 @@ public void TestTensorFlowStatic()
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
+        [TensorFlowFact]
         public void TestTensorFlowStaticWithSchema()
         {
             const string modelLocation = "cifar_model/frozen_model.pb";
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs b/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
index 64179fb039..2749961baf 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/MatrixFactorizationTests.cs
@@ -10,6 +10,7 @@
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Trainers;
 using Xunit;
 
@@ -17,8 +18,8 @@ namespace Microsoft.ML.Tests.TrainerEstimators
 {
     public partial class TrainerEstimators : TestDataPipeBase
     {
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
-        public void MatrixFactorizationEstimator()
+        [MatrixFactorizationFact]
+        public void MatrixFactorization_Estimator()
         {
             string labelColumnName = "Label";
             string matrixColumnIndexColumnName = "Col";
@@ -50,7 +51,7 @@ public void MatrixFactorizationEstimator()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
+        [MatrixFactorizationFact]
         public void MatrixFactorizationSimpleTrainAndPredict()
         {
             var mlContext = new MLContext(seed: 1, conc: 1);
@@ -189,7 +190,7 @@ internal class MatrixElementForScore
             public float Score;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
+        [MatrixFactorizationFact]
         public void MatrixFactorizationInMemoryData()
         {
             // Create an in-memory matrix as a list of tuples (column index, row index, value).
@@ -278,7 +279,7 @@ internal class MatrixElementZeroBasedForScore
             public float Score;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
+        [MatrixFactorizationFact]
         public void MatrixFactorizationInMemoryDataZeroBaseIndex()
         {
             // Create an in-memory matrix as a list of tuples (column index, row index, value).
@@ -392,7 +393,7 @@ private class OneClassMatrixElementZeroBasedForScore
             public float Score;
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
+        [MatrixFactorizationFact]
         public void OneClassMatrixFactorizationInMemoryDataZeroBaseIndex()
         {
             // Create an in-memory matrix as a list of tuples (column index, row index, value). For one-class matrix
@@ -464,7 +465,7 @@ public void OneClassMatrixFactorizationInMemoryDataZeroBaseIndex()
             CompareNumbersWithTolerance(0.141411, testResults[1].Score, digitsOfPrecision: 5);
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // This test is being fixed as part of issue #1441.
+        [MatrixFactorizationFact]
         public void MatrixFactorizationBackCompat()
         {
             // This test is meant to check backwards compatibility after the change that removed Min and Contiguous from KeyType.
diff --git a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
index 9f3dfe20ee..9e995ceee0 100644
--- a/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
+++ b/test/Microsoft.ML.Tests/TrainerEstimators/TreeEstimators.cs
@@ -10,6 +10,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Transforms.Conversions;
 using Xunit;
@@ -42,7 +43,7 @@ public void FastTreeBinaryEstimator()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGBMBinaryEstimator()
         {
             var (pipe, dataView) = GetBinaryClassificationPipeline();
@@ -128,7 +129,7 @@ public void FastTreeRankerEstimator()
         /// <summary>
         /// LightGbmRankingTrainer TrainerEstimator test 
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGBMRankerEstimator()
         {
             var (pipe, dataView) = GetRankingPipeline();
@@ -161,7 +162,7 @@ public void FastTreeRegressorEstimator()
         /// <summary>
         /// LightGbmRegressorTrainer TrainerEstimator test 
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGBMRegressorEstimator()
         {
             var dataView = GetRegressionPipeline();
@@ -237,7 +238,7 @@ public void FastForestRegressorEstimator()
         /// <summary>
         /// LightGbmMulticlass TrainerEstimator test 
         /// </summary>
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmMultiClassEstimator()
         {
             var (pipeline, dataView) = GetMultiClassPipeline();
@@ -369,7 +370,7 @@ private void LightGbmHelper(bool useSoftmax, out string modelString, out List<Gb
             }
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmMultiClassEstimatorCompareOva()
         {
             // Train ML.NET LightGBM and native LightGBM and apply the trained models to the training set.
@@ -399,7 +400,7 @@ public void LightGbmMultiClassEstimatorCompareOva()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmMultiClassEstimatorCompareSoftMax()
         {
             // Train ML.NET LightGBM and native LightGBM and apply the trained models to the training set.
@@ -428,7 +429,7 @@ public void LightGbmMultiClassEstimatorCompareSoftMax()
             Done();
         }
 
-        [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] // LightGBM is 64-bit only
+        [LightGBMFact]
         public void LightGbmInDifferentCulture()
         {
             var currentCulture = Thread.CurrentThread.CurrentCulture;
diff --git a/test/Microsoft.ML.Tests/Transformers/NormalizerTests.cs b/test/Microsoft.ML.Tests/Transformers/NormalizerTests.cs
index 842575b7e0..873bc5333e 100644
--- a/test/Microsoft.ML.Tests/Transformers/NormalizerTests.cs
+++ b/test/Microsoft.ML.Tests/Transformers/NormalizerTests.cs
@@ -11,6 +11,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.StaticPipe;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.Tools;
 using Microsoft.ML.Transforms;
 using Microsoft.ML.Transforms.Normalizers;
@@ -415,7 +416,7 @@ public void TestLpNormOldSavingAndLoading()
             }
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void GcnWorkout()
         {
             string dataSource = GetDataPath(TestDatasets.generatedRegressionDataset.trainFilename);
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
index 5ff02e2723..e79da61465 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
@@ -4,6 +4,7 @@
 
 using System.IO;
 using System.Linq;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.TimeSeriesProcessing;
 using Xunit;
 using Xunit.Abstractions;
@@ -70,7 +71,7 @@ public void SavePipeSsaSpike()
             Done();
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]  // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void SavePipeSsaSpikeNoData()
         {
             string pathData = DeleteOutputPath("SavePipe", "SsaSpikeNoData.txt");
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
index 5f9863b5d1..9b64a68abd 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
@@ -7,6 +7,7 @@
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 using Microsoft.ML.RunTests;
+using Microsoft.ML.TestFramework.Attributes;
 using Microsoft.ML.TimeSeries;
 using Microsoft.ML.TimeSeriesProcessing;
 using Xunit;
@@ -84,7 +85,7 @@ public void ChangeDetection()
             }
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))] // netcore3.0 output differs from Baseline
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void ChangePointDetectionWithSeasonality()
         {
             var env = new MLContext(conc: 1);
@@ -134,7 +135,7 @@ public void ChangePointDetectionWithSeasonality()
             }
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void ChangePointDetectionWithSeasonalityPredictionEngineNoColumn()
         {
             const int ChangeHistorySize = 10;
@@ -210,7 +211,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngineNoColumn()
             Assert.Equal(0.12216401100158691, prediction2.Change[1], precision: 5); // Raw score
         }
 
-        [ConditionalFact(typeof(BaseTestBaseline), nameof(BaseTestBaseline.LessThanNetCore30OrNotNetCore))]
+        [LessThanNetCore30OrNotNetCoreFact("netcoreapp3.0 output differs from Baseline")]
         public void ChangePointDetectionWithSeasonalityPredictionEngine()
         {
             const int ChangeHistorySize = 10;

From f269adcd30a33feeb969c17c2a9f9db235113cb1 Mon Sep 17 00:00:00 2001
From: Senja Filipi <sfilipi@users.noreply.github.com>
Date: Tue, 12 Feb 2019 08:02:50 -0800
Subject: [PATCH 13/23] Towards 2326: Microsoft.ML.Ensemble and
 Microsoft.ML.TimeSeries namespace rationalization (#2491)

* moving Microsoft.ML.TimeSeriesProcessing, and Microsoft.ML.TimeSeries to Microsoft.ML.Transforms.TimeSeries

* Microsoft.ML.Ensemble to Microsoft.ML.Trainers.Ensemble
---
 .../IidChangePointDetectorTransform.cs        |  4 +--
 .../Dynamic/IidSpikeDetectorTransform.cs      |  4 +--
 .../SsaChangePointDetectorTransform.cs        |  4 +--
 .../Dynamic/SsaSpikeDetectorTransform.cs      |  4 +--
 src/Microsoft.ML.Ensemble/Batch.cs            |  2 +-
 src/Microsoft.ML.Ensemble/EnsembleUtils.cs    |  2 +-
 .../EntryPoints/CreateEnsemble.cs             |  5 ++-
 .../EntryPoints/DiversityMeasure.cs           |  7 ++--
 .../EntryPoints/Ensemble.cs                   |  4 +--
 .../EntryPoints/FeatureSelector.cs            |  7 ++--
 .../EntryPoints/OutputCombiner.cs             |  5 ++-
 .../EntryPoints/PipelineEnsemble.cs           |  4 +--
 .../EntryPoints/SubModelSelector.cs           |  7 ++--
 .../FeatureSubsetModel.cs                     |  2 +-
 .../OutputCombiners/Average.cs                |  4 +--
 .../OutputCombiners/BaseAverager.cs           |  2 +-
 .../OutputCombiners/BaseMultiAverager.cs      |  2 +-
 .../OutputCombiners/BaseMultiCombiner.cs      |  2 +-
 .../OutputCombiners/BaseScalarStacking.cs     |  2 +-
 .../OutputCombiners/BaseStacking.cs           |  2 +-
 .../OutputCombiners/IOutputCombiner.cs        |  2 +-
 .../OutputCombiners/Median.cs                 |  4 +--
 .../OutputCombiners/MultiAverage.cs           |  4 +--
 .../OutputCombiners/MultiMedian.cs            |  4 +--
 .../OutputCombiners/MultiStacking.cs          |  4 +--
 .../OutputCombiners/MultiVoting.cs            |  4 +--
 .../OutputCombiners/MultiWeightedAverage.cs   |  4 +--
 .../OutputCombiners/RegressionStacking.cs     |  4 +--
 .../OutputCombiners/Stacking.cs               |  4 +--
 .../OutputCombiners/Voting.cs                 |  4 +--
 .../OutputCombiners/WeightedAverage.cs        |  4 +--
 src/Microsoft.ML.Ensemble/PipelineEnsemble.cs |  5 ++-
 .../BaseDisagreementDiversityMeasure.cs       |  2 +-
 .../DisagreementDiversityMeasure.cs           |  6 ++--
 .../DiversityMeasure/ModelDiversityMetric.cs  |  2 +-
 .../MultiDisagreementDiversityMeasure.cs      |  6 ++--
 .../RegressionDisagreementDiversityMeasure.cs |  6 ++--
 .../FeatureSelector/AllFeatureSelector.cs     |  6 ++--
 .../FeatureSelector/RandomFeatureSelector.cs  |  6 ++--
 .../Selector/IDiversityMeasure.cs             |  4 +--
 .../Selector/IFeatureSelector.cs              |  2 +-
 .../Selector/ISubModelSelector.cs             |  2 +-
 .../Selector/ISubsetSelector.cs               |  2 +-
 .../Selector/SubModelSelector/AllSelector.cs  |  6 ++--
 .../SubModelSelector/AllSelectorMultiClass.cs |  6 ++--
 .../BaseBestPerformanceSelector.cs            |  2 +-
 .../SubModelSelector/BaseDiverseSelector.cs   |  4 +--
 .../SubModelSelector/BaseSubModelSelector.cs  |  2 +-
 .../BestDiverseSelectorBinary.cs              |  8 ++---
 .../BestDiverseSelectorMultiClass.cs          |  8 ++---
 .../BestDiverseSelectorRegression.cs          |  8 ++---
 .../BestPerformanceRegressionSelector.cs      |  6 ++--
 .../BestPerformanceSelector.cs                |  6 ++--
 .../BestPerformanceSelectorMultiClass.cs      |  6 ++--
 .../SubModelSelector/SubModelDataSelector.cs  |  2 +-
 .../SubsetSelector/AllInstanceSelector.cs     |  6 ++--
 .../SubsetSelector/BaseSubsetSelector.cs      |  2 +-
 .../SubsetSelector/BootstrapSelector.cs       |  6 ++--
 .../SubsetSelector/RandomPartitionSelector.cs |  6 ++--
 src/Microsoft.ML.Ensemble/Subset.cs           |  2 +-
 .../Trainer/Binary/EnsembleTrainer.cs         |  7 ++--
 .../EnsembleDistributionModelParameters.cs    |  5 ++-
 .../Trainer/EnsembleModelParameters.cs        |  5 ++-
 .../Trainer/EnsembleModelParametersBase.cs    |  4 +--
 .../Trainer/EnsembleTrainerBase.cs            |  6 ++--
 .../Trainer/IModelCombiner.cs                 |  2 +-
 .../EnsembleMultiClassModelParameters.cs      |  7 ++--
 .../MulticlassDataPartitionEnsembleTrainer.cs |  7 ++--
 .../Regression/RegressionEnsembleTrainer.cs   |  7 ++--
 .../TreeEnsemble/TreeEnsembleCombiner.cs      |  3 +-
 .../TimeSeriesStatic.cs                       |  2 +-
 ...AdaptiveSingularSpectrumSequenceModeler.cs |  5 ++-
 src/Microsoft.ML.TimeSeries/EigenUtils.cs     |  2 +-
 .../ExponentialAverageTransform.cs            |  4 +--
 .../ExtensionsCatalog.cs                      |  2 +-
 src/Microsoft.ML.TimeSeries/FftUtils.cs       |  2 +-
 .../IidAnomalyDetectionBase.cs                |  3 +-
 .../IidChangePointDetector.cs                 |  5 ++-
 .../IidSpikeDetector.cs                       |  5 ++-
 .../MovingAverageTransform.cs                 |  4 +--
 .../PValueTransform.cs                        |  4 +--
 .../PercentileThresholdTransform.cs           |  4 +--
 .../PolynomialUtils.cs                        |  2 +-
 .../PredictionFunction.cs                     |  2 +-
 .../SequenceModelerBase.cs                    |  2 +-
 ...SequentialAnomalyDetectionTransformBase.cs |  4 +--
 .../SequentialTransformBase.cs                |  2 +-
 .../SequentialTransformerBase.cs              |  4 +--
 .../SlidingWindowTransform.cs                 |  4 +--
 .../SlidingWindowTransformBase.cs             |  6 ++--
 .../SsaAnomalyDetectionBase.cs                |  4 +--
 .../SsaChangePointDetector.cs                 |  5 ++-
 .../SsaSpikeDetector.cs                       |  5 ++-
 .../TimeSeriesProcessing.cs                   | 34 ++++++++++++------
 .../TimeSeriesUtils.cs                        |  2 +-
 .../TrajectoryMatrix.cs                       |  2 +-
 .../Common/EntryPoints/core_ep-list.tsv       | 36 +++++++++----------
 .../UnitTests/TestEntryPoints.cs              |  5 ++-
 .../TestPredictors.cs                         |  2 +-
 .../EnvironmentExtensions.cs                  |  2 +-
 .../TimeSeries.cs                             |  2 +-
 .../TimeSeriesDirectApi.cs                    |  3 +-
 .../TimeSeriesEstimatorTests.cs               |  2 +-
 103 files changed, 224 insertions(+), 253 deletions(-)

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
index d7dbc8553b..9b34510bd0 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/IidChangePointDetectorTransform.cs
@@ -5,11 +5,9 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML.Samples.Dynamic
 {
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
index c03449be17..c2fedc5275 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/IidSpikeDetectorTransform.cs
@@ -1,11 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML.Samples.Dynamic
 {
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
index 19bb5e75c5..223bab2277 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaChangePointDetectorTransform.cs
@@ -1,11 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML.Samples.Dynamic
 {
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
index 217ddb69ed..9ebf1a41d2 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/SsaSpikeDetectorTransform.cs
@@ -1,11 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML.Samples.Dynamic
 {
diff --git a/src/Microsoft.ML.Ensemble/Batch.cs b/src/Microsoft.ML.Ensemble/Batch.cs
index caaf6bf4f3..756b095c72 100644
--- a/src/Microsoft.ML.Ensemble/Batch.cs
+++ b/src/Microsoft.ML.Ensemble/Batch.cs
@@ -4,7 +4,7 @@
 
 using Microsoft.ML.Data;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal sealed class Batch
     {
diff --git a/src/Microsoft.ML.Ensemble/EnsembleUtils.cs b/src/Microsoft.ML.Ensemble/EnsembleUtils.cs
index 2af9b96f3b..342a33b9bd 100644
--- a/src/Microsoft.ML.Ensemble/EnsembleUtils.cs
+++ b/src/Microsoft.ML.Ensemble/EnsembleUtils.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal static class EnsembleUtils
     {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
index baad8e2ca9..6ce5673797 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/CreateEnsemble.cs
@@ -10,14 +10,13 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(void), typeof(EnsembleCreator), null, typeof(SignatureEntryPointModule), "CreateEnsemble")]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// A component to combine given models into an ensemble model.
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs b/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
index 069cce2556..075b3659ec 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/DiversityMeasure.cs
@@ -2,16 +2,15 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 
 [assembly: EntryPointModule(typeof(DisagreementDiversityFactory))]
 [assembly: EntryPointModule(typeof(RegressionDisagreementDiversityFactory))]
 [assembly: EntryPointModule(typeof(MultiDisagreementDiversityFactory))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     [TlcModule.Component(Name = DisagreementDiversityMeasure.LoadName, FriendlyName = DisagreementDiversityMeasure.UserName)]
     internal sealed class DisagreementDiversityFactory : ISupportBinaryDiversityMeasureFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
index 4aa0ab10ce..afec3af471 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/Ensemble.cs
@@ -3,12 +3,12 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML;
-using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(void), typeof(Ensemble), null, typeof(SignatureEntryPointModule), "TrainEnsemble")]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal static class Ensemble
     {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs b/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
index 66ae78ecb5..8af31a0723 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/FeatureSelector.cs
@@ -2,15 +2,14 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.FeatureSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.FeatureSelector;
 
 [assembly: EntryPointModule(typeof(AllFeatureSelectorFactory))]
 [assembly: EntryPointModule(typeof(RandomFeatureSelector))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     [TlcModule.Component(Name = AllFeatureSelector.LoadName, FriendlyName = AllFeatureSelector.UserName)]
     public sealed class AllFeatureSelectorFactory : ISupportFeatureSelectorFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs b/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
index da10f30de4..5af5cdf487 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/OutputCombiner.cs
@@ -2,9 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: EntryPointModule(typeof(AverageFactory))]
 [assembly: EntryPointModule(typeof(MedianFactory))]
@@ -18,7 +17,7 @@
 [assembly: EntryPointModule(typeof(VotingFactory))]
 [assembly: EntryPointModule(typeof(WeightedAverage))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     [TlcModule.Component(Name = Average.LoadName, FriendlyName = Average.UserName)]
     public sealed class AverageFactory : ISupportBinaryOutputCombinerFactory, ISupportRegressionOutputCombinerFactory
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs b/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
index 9452a9d064..2bafa85a2f 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/PipelineEnsemble.cs
@@ -4,13 +4,13 @@
 
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Calibration;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: EntryPointModule(typeof(PipelineEnsemble))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal static class PipelineEnsemble
     {
diff --git a/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs b/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
index b749501f76..867833f999 100644
--- a/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
+++ b/src/Microsoft.ML.Ensemble/EntryPoints/SubModelSelector.cs
@@ -2,10 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: EntryPointModule(typeof(AllSelectorFactory))]
 [assembly: EntryPointModule(typeof(AllSelectorMultiClassFactory))]
@@ -16,7 +15,7 @@
 [assembly: EntryPointModule(typeof(BestPerformanceSelector))]
 [assembly: EntryPointModule(typeof(BestPerformanceSelectorMultiClass))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     [TlcModule.Component(Name = AllSelector.LoadName, FriendlyName = AllSelector.UserName)]
     public sealed class AllSelectorFactory : ISupportBinarySubModelSelectorFactory, ISupportRegressionSubModelSelectorFactory
diff --git a/src/Microsoft.ML.Ensemble/FeatureSubsetModel.cs b/src/Microsoft.ML.Ensemble/FeatureSubsetModel.cs
index 733573d39f..d583a1b125 100644
--- a/src/Microsoft.ML.Ensemble/FeatureSubsetModel.cs
+++ b/src/Microsoft.ML.Ensemble/FeatureSubsetModel.cs
@@ -6,7 +6,7 @@
 using System.Collections.Generic;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal sealed class FeatureSubsetModel<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
index 4582d4f4a9..e78e63cecc 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Average.cs
@@ -4,13 +4,13 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(Average), null, typeof(SignatureCombiner), Average.UserName)]
 [assembly: LoadableClass(typeof(Average), null, typeof(SignatureLoadModel), Average.UserName, Average.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public sealed class Average : BaseAverager, IRegressionOutputCombiner
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
index 0b454be238..1d8f56e3fe 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseAverager.cs
@@ -5,7 +5,7 @@
 using System;
 using Microsoft.ML.Model;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public abstract class BaseAverager : IBinaryOutputCombiner, ICanSaveModel
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiAverager.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiAverager.cs
index 73e6f4ea7e..044ccfb44e 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiAverager.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiAverager.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Numeric;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public abstract class BaseMultiAverager : BaseMultiCombiner
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
index 737cbfd649..d0ee4583b3 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseMultiCombiner.cs
@@ -9,7 +9,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Numeric;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public abstract class BaseMultiCombiner : IMultiClassOutputCombiner, ICanSaveModel
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseScalarStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseScalarStacking.cs
index ba75e05080..59700cb18c 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseScalarStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseScalarStacking.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal abstract class BaseScalarStacking : BaseStacking<Single>
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
index c081feee14..a172a6bce4 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/BaseStacking.cs
@@ -13,7 +13,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Training;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal abstract class BaseStacking<TOutput> : IStackingTrainer<TOutput>, ICanSaveModel
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/IOutputCombiner.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/IOutputCombiner.cs
index bbbbecf217..e054cd2fa8 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/IOutputCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/IOutputCombiner.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// Signature for combiners.
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
index 5173216711..88a9d73ca3 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Median.cs
@@ -4,14 +4,14 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(Median), null, typeof(SignatureCombiner), Median.UserName, Median.LoadName)]
 [assembly: LoadableClass(typeof(Median), null, typeof(SignatureLoadModel), Median.UserName, Median.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// Generic interface for combining outputs of multiple models
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
index be3bd7dfa2..3cf424b50f 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiAverage.cs
@@ -5,16 +5,16 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(MultiAverage), typeof(MultiAverage.Arguments), typeof(SignatureCombiner),
     Average.UserName, MultiAverage.LoadName)]
 [assembly: LoadableClass(typeof(MultiAverage), null, typeof(SignatureLoadModel), Average.UserName,
     MultiAverage.LoadName, MultiAverage.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public sealed class MultiAverage : BaseMultiAverager
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
index 94b0f38ae8..3b44413397 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiMedian.cs
@@ -5,16 +5,16 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(MultiMedian), typeof(MultiMedian.Arguments), typeof(SignatureCombiner),
     Median.UserName, MultiMedian.LoadName)]
 [assembly: LoadableClass(typeof(MultiMedian), null, typeof(SignatureLoadModel), Median.UserName, MultiMedian.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// Generic interface for combining outputs of multiple models
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
index f2bb13d029..01011a99d4 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiStacking.cs
@@ -6,10 +6,10 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(MultiStacking), typeof(MultiStacking.Arguments), typeof(SignatureCombiner),
    Stacking.UserName, MultiStacking.LoadName)]
@@ -17,7 +17,7 @@
 [assembly: LoadableClass(typeof(MultiStacking), null, typeof(SignatureLoadModel),
     Stacking.UserName, MultiStacking.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TVectorPredictor = IPredictorProducing<VBuffer<Single>>;
     internal sealed class MultiStacking : BaseStacking<VBuffer<Single>>, IMultiClassOutputCombiner
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
index a222606ae1..f943a4176c 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiVoting.cs
@@ -5,15 +5,15 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 using Microsoft.ML.Numeric;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(MultiVoting), null, typeof(SignatureCombiner), Voting.UserName, MultiVoting.LoadName)]
 [assembly: LoadableClass(typeof(MultiVoting), null, typeof(SignatureLoadModel), Voting.UserName, MultiVoting.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     // REVIEW: Why is MultiVoting based on BaseMultiCombiner? Normalizing the model outputs
     // is senseless, so the base adds no real functionality.
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
index 161487650a..aecc3963bc 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/MultiWeightedAverage.cs
@@ -6,10 +6,10 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(MultiWeightedAverage), typeof(MultiWeightedAverage.Arguments), typeof(SignatureCombiner),
     MultiWeightedAverage.UserName, MultiWeightedAverage.LoadName)]
@@ -17,7 +17,7 @@
 [assembly: LoadableClass(typeof(MultiWeightedAverage), null, typeof(SignatureLoadModel),
     MultiWeightedAverage.UserName, MultiWeightedAverage.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// Generic interface for combining outputs of multiple models
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
index 239e386ebf..ae9ef67db8 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/RegressionStacking.cs
@@ -4,10 +4,10 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(RegressionStacking), typeof(RegressionStacking.Arguments), typeof(SignatureCombiner),
     Stacking.UserName, RegressionStacking.LoadName)]
@@ -15,7 +15,7 @@
 [assembly: LoadableClass(typeof(RegressionStacking), null, typeof(SignatureLoadModel),
     Stacking.UserName, RegressionStacking.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TScalarPredictor = IPredictorProducing<Single>;
 
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
index 9999544c6c..93fe1d3240 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Stacking.cs
@@ -5,15 +5,15 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(Stacking), typeof(Stacking.Arguments), typeof(SignatureCombiner), Stacking.UserName, Stacking.LoadName)]
 [assembly: LoadableClass(typeof(Stacking), null, typeof(SignatureLoadModel), Stacking.UserName, Stacking.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TScalarPredictor = IPredictorProducing<Single>;
     internal sealed class Stacking : BaseScalarStacking, IBinaryOutputCombiner
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
index 3700782957..17c569d8ce 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/Voting.cs
@@ -4,14 +4,14 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(Voting), null, typeof(SignatureCombiner), Voting.UserName, Voting.LoadName)]
 [assembly: LoadableClass(typeof(Voting), null, typeof(SignatureLoadModel), Voting.UserName, Voting.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public sealed class Voting : IBinaryOutputCombiner, ICanSaveModel
     {
diff --git a/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs b/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
index e6164b1914..e5aa458768 100644
--- a/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
+++ b/src/Microsoft.ML.Ensemble/OutputCombiners/WeightedAverage.cs
@@ -6,10 +6,10 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(WeightedAverage), typeof(WeightedAverage.Arguments), typeof(SignatureCombiner),
     WeightedAverage.UserName, WeightedAverage.LoadName)]
@@ -17,7 +17,7 @@
 [assembly: LoadableClass(typeof(WeightedAverage), null, typeof(SignatureLoadModel),
      WeightedAverage.UserName, WeightedAverage.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble.OutputCombiners
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public sealed class WeightedAverage : BaseAverager, IWeightedAverager
     {
diff --git a/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs b/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
index 6f22df98c5..4f44a42204 100644
--- a/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
+++ b/src/Microsoft.ML.Ensemble/PipelineEnsemble.cs
@@ -10,18 +10,17 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Calibration;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(SchemaBindablePipelineEnsembleBase), null, typeof(SignatureLoadModel),
     SchemaBindablePipelineEnsembleBase.UserName, SchemaBindablePipelineEnsembleBase.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// This class represents an ensemble predictor, where each predictor has its own featurization pipeline. It is
diff --git a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/BaseDisagreementDiversityMeasure.cs b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/BaseDisagreementDiversityMeasure.cs
index b4106a7b1d..c2f40e0b1a 100644
--- a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/BaseDisagreementDiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/BaseDisagreementDiversityMeasure.cs
@@ -6,7 +6,7 @@
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 
-namespace Microsoft.ML.Ensemble.Selector.DiversityMeasure
+namespace Microsoft.ML.Trainers.Ensemble.DiversityMeasure
 {
     internal abstract class BaseDisagreementDiversityMeasure<TOutput> : IDiversityMeasure<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/DisagreementDiversityMeasure.cs b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/DisagreementDiversityMeasure.cs
index bb0127003f..4cb1cbd883 100644
--- a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/DisagreementDiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/DisagreementDiversityMeasure.cs
@@ -4,13 +4,13 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 
 [assembly: LoadableClass(typeof(DisagreementDiversityMeasure), null, typeof(SignatureEnsembleDiversityMeasure),
     DisagreementDiversityMeasure.UserName, DisagreementDiversityMeasure.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.DiversityMeasure
+namespace Microsoft.ML.Trainers.Ensemble.DiversityMeasure
 {
     internal sealed class DisagreementDiversityMeasure : BaseDisagreementDiversityMeasure<Single>, IBinaryDiversityMeasure
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/ModelDiversityMetric.cs b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/ModelDiversityMetric.cs
index b182ad9963..04c9e7f2d2 100644
--- a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/ModelDiversityMetric.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/ModelDiversityMetric.cs
@@ -4,7 +4,7 @@
 
 using System;
 
-namespace Microsoft.ML.Ensemble.Selector.DiversityMeasure
+namespace Microsoft.ML.Trainers.Ensemble.DiversityMeasure
 {
     internal sealed class ModelDiversityMetric<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/MultiDisagreementDiversityMeasure.cs b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/MultiDisagreementDiversityMeasure.cs
index ffb6839818..ccdd4d6b0b 100644
--- a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/MultiDisagreementDiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/MultiDisagreementDiversityMeasure.cs
@@ -5,14 +5,14 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.Numeric;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 
 [assembly: LoadableClass(typeof(MultiDisagreementDiversityMeasure), null, typeof(SignatureEnsembleDiversityMeasure),
     DisagreementDiversityMeasure.UserName, MultiDisagreementDiversityMeasure.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.DiversityMeasure
+namespace Microsoft.ML.Trainers.Ensemble.DiversityMeasure
 {
     internal sealed class MultiDisagreementDiversityMeasure : BaseDisagreementDiversityMeasure<VBuffer<Single>>, IMulticlassDiversityMeasure
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/RegressionDisagreementDiversityMeasure.cs b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/RegressionDisagreementDiversityMeasure.cs
index 2b60e44f1c..c1b37411b6 100644
--- a/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/RegressionDisagreementDiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/DiversityMeasure/RegressionDisagreementDiversityMeasure.cs
@@ -4,13 +4,13 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 
 [assembly: LoadableClass(typeof(RegressionDisagreementDiversityMeasure), null, typeof(SignatureEnsembleDiversityMeasure),
     DisagreementDiversityMeasure.UserName, RegressionDisagreementDiversityMeasure.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.DiversityMeasure
+namespace Microsoft.ML.Trainers.Ensemble.DiversityMeasure
 {
     internal sealed class RegressionDisagreementDiversityMeasure : BaseDisagreementDiversityMeasure<Single>, IRegressionDiversityMeasure
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/AllFeatureSelector.cs b/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/AllFeatureSelector.cs
index f2ffadf877..52c0a4752b 100644
--- a/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/AllFeatureSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/AllFeatureSelector.cs
@@ -5,13 +5,13 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.FeatureSelector;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.FeatureSelector;
 
 [assembly: LoadableClass(typeof(AllFeatureSelector), null, typeof(SignatureEnsembleFeatureSelector),
     AllFeatureSelector.UserName, AllFeatureSelector.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.FeatureSelector
+namespace Microsoft.ML.Trainers.Ensemble.FeatureSelector
 {
     internal sealed class AllFeatureSelector : IFeatureSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/RandomFeatureSelector.cs b/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/RandomFeatureSelector.cs
index 93c4bd7603..5841d8c126 100644
--- a/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/RandomFeatureSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/FeatureSelector/RandomFeatureSelector.cs
@@ -7,15 +7,15 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.FeatureSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.FeatureSelector;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(typeof(RandomFeatureSelector), typeof(RandomFeatureSelector.Arguments),
     typeof(SignatureEnsembleFeatureSelector), RandomFeatureSelector.UserName, RandomFeatureSelector.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.FeatureSelector
+namespace Microsoft.ML.Trainers.Ensemble.FeatureSelector
 {
     internal class RandomFeatureSelector : IFeatureSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/IDiversityMeasure.cs b/src/Microsoft.ML.Ensemble/Selector/IDiversityMeasure.cs
index 8ac30f3818..96642ccc8f 100644
--- a/src/Microsoft.ML.Ensemble/Selector/IDiversityMeasure.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/IDiversityMeasure.cs
@@ -6,10 +6,10 @@
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 
-namespace Microsoft.ML.Ensemble.Selector
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal interface IDiversityMeasure<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/IFeatureSelector.cs b/src/Microsoft.ML.Ensemble/Selector/IFeatureSelector.cs
index e4eb986294..6ccc6da5d7 100644
--- a/src/Microsoft.ML.Ensemble/Selector/IFeatureSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/IFeatureSelector.cs
@@ -6,7 +6,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 
-namespace Microsoft.ML.Ensemble.Selector
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal interface IFeatureSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/ISubModelSelector.cs b/src/Microsoft.ML.Ensemble/Selector/ISubModelSelector.cs
index e5b35082ee..6f910f6a44 100644
--- a/src/Microsoft.ML.Ensemble/Selector/ISubModelSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/ISubModelSelector.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 
-namespace Microsoft.ML.Ensemble.Selector
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal interface ISubModelSelector<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/ISubsetSelector.cs b/src/Microsoft.ML.Ensemble/Selector/ISubsetSelector.cs
index 6ba7002508..8ffef7aba1 100644
--- a/src/Microsoft.ML.Ensemble/Selector/ISubsetSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/ISubsetSelector.cs
@@ -7,7 +7,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 
-namespace Microsoft.ML.Ensemble.Selector
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal interface ISubsetSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelector.cs
index f88df3bfee..ce62ee9180 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelector.cs
@@ -4,12 +4,12 @@
 
 using System;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(AllSelector), null, typeof(SignatureEnsembleSubModelSelector), AllSelector.UserName, AllSelector.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class AllSelector : BaseSubModelSelector<Single>, IBinarySubModelSelector, IRegressionSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelectorMultiClass.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelectorMultiClass.cs
index 2158b05733..6905579c3b 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelectorMultiClass.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/AllSelectorMultiClass.cs
@@ -5,13 +5,13 @@
 using System;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(AllSelectorMultiClass), null, typeof(SignatureEnsembleSubModelSelector),
     AllSelectorMultiClass.UserName, AllSelectorMultiClass.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class AllSelectorMultiClass : BaseSubModelSelector<VBuffer<Single>>, IMulticlassSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseBestPerformanceSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseBestPerformanceSelector.cs
index 55add475f5..a13ef47b35 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseBestPerformanceSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseBestPerformanceSelector.cs
@@ -8,7 +8,7 @@
 using System.Reflection;
 using Microsoft.ML.CommandLine;
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal abstract class BaseBestPerformanceSelector<TOutput> : SubModelDataSelector<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseDiverseSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseDiverseSelector.cs
index e1edba726e..8f6d783c76 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseDiverseSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseDiverseSelector.cs
@@ -6,11 +6,11 @@
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
 using Microsoft.ML.Internal.Utilities;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
 using Microsoft.ML.Training;
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal abstract class BaseDiverseSelector<TOutput, TDiversityMetric> : SubModelDataSelector<TOutput>
         where TDiversityMetric : class, IDiversityMeasure<TOutput>
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseSubModelSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseSubModelSelector.cs
index 8d1189d049..87a61192cd 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseSubModelSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BaseSubModelSelector.cs
@@ -8,7 +8,7 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.Data;
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal abstract class BaseSubModelSelector<TOutput> : ISubModelSelector<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
index 3b2204af4a..869da7307e 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorBinary.cs
@@ -7,16 +7,16 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestDiverseSelectorBinary), typeof(BestDiverseSelectorBinary.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestDiverseSelectorBinary.UserName, BestDiverseSelectorBinary.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestDiverseSelectorBinary : BaseDiverseSelector<Single, DisagreementDiversityMeasure>, IBinarySubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
index 8bae59d1f5..9a39b4f5b3 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorMultiClass.cs
@@ -8,16 +8,16 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestDiverseSelectorMultiClass), typeof(BestDiverseSelectorMultiClass.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestDiverseSelectorMultiClass.UserName, BestDiverseSelectorMultiClass.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestDiverseSelectorMultiClass : BaseDiverseSelector<VBuffer<Single>, IDiversityMeasure<VBuffer<Single>>>, IMulticlassSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
index 132f691034..2022e7d057 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestDiverseSelectorRegression.cs
@@ -7,16 +7,16 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.DiversityMeasure;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.DiversityMeasure;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestDiverseSelectorRegression), typeof(BestDiverseSelectorRegression.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestDiverseSelectorRegression.UserName, BestDiverseSelectorRegression.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestDiverseSelectorRegression : BaseDiverseSelector<Single, RegressionDisagreementDiversityMeasure>, IRegressionSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceRegressionSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceRegressionSelector.cs
index be7b7e8f35..de6fe8874d 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceRegressionSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceRegressionSelector.cs
@@ -6,15 +6,15 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestPerformanceRegressionSelector), typeof(BestPerformanceRegressionSelector.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestPerformanceRegressionSelector.UserName, BestPerformanceRegressionSelector.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestPerformanceRegressionSelector : BaseBestPerformanceSelector<Single>, IRegressionSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelector.cs
index 9b24798276..f11b30556f 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelector.cs
@@ -6,15 +6,15 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestPerformanceSelector), typeof(BestPerformanceSelector.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestPerformanceSelector.UserName, BestPerformanceSelector.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestPerformanceSelector : BaseBestPerformanceSelector<Single>, IBinarySubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelectorMultiClass.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelectorMultiClass.cs
index 36f9635c87..34ce8719db 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelectorMultiClass.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/BestPerformanceSelectorMultiClass.cs
@@ -6,15 +6,15 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubModelSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubModelSelector;
 
 [assembly: LoadableClass(typeof(BestPerformanceSelectorMultiClass), typeof(BestPerformanceSelectorMultiClass.Arguments),
     typeof(SignatureEnsembleSubModelSelector), BestPerformanceSelectorMultiClass.UserName, BestPerformanceSelectorMultiClass.LoadName)]
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal sealed class BestPerformanceSelectorMultiClass : BaseBestPerformanceSelector<VBuffer<Single>>, IMulticlassSubModelSelector
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/SubModelDataSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/SubModelDataSelector.cs
index 0ba6497f25..ff8dd74354 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/SubModelDataSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubModelSelector/SubModelDataSelector.cs
@@ -6,7 +6,7 @@
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Internal.Internallearn;
 
-namespace Microsoft.ML.Ensemble.Selector.SubModelSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubModelSelector
 {
     internal abstract class SubModelDataSelector<TOutput> : BaseSubModelSelector<TOutput>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/AllInstanceSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/AllInstanceSelector.cs
index 43203e9ca9..1c1c15c3cd 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/AllInstanceSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/AllInstanceSelector.cs
@@ -5,16 +5,16 @@
 using System;
 using System.Collections.Generic;
 using Microsoft.ML;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubsetSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubsetSelector;
 
 [assembly: LoadableClass(typeof(AllInstanceSelector), typeof(AllInstanceSelector.Arguments),
     typeof(SignatureEnsembleDataSelector), AllInstanceSelector.UserName, AllInstanceSelector.LoadName)]
 
 [assembly: EntryPointModule(typeof(AllInstanceSelector))]
 
-namespace Microsoft.ML.Ensemble.Selector.SubsetSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubsetSelector
 {
     internal sealed class AllInstanceSelector : BaseSubsetSelector<AllInstanceSelector.Arguments>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
index 8504525012..3305574d36 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BaseSubsetSelector.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Transforms;
 
-namespace Microsoft.ML.Ensemble.Selector.SubsetSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubsetSelector
 {
     internal abstract class BaseSubsetSelector<TArgs> : ISubsetSelector
         where TArgs : BaseSubsetSelector<TArgs>.ArgumentsBase
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BootstrapSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BootstrapSelector.cs
index c3a98e47c8..8006ba2195 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BootstrapSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/BootstrapSelector.cs
@@ -6,9 +6,9 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubsetSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubsetSelector;
 using Microsoft.ML.Transforms;
 
 [assembly: LoadableClass(typeof(BootstrapSelector), typeof(BootstrapSelector.Arguments),
@@ -16,7 +16,7 @@
 
 [assembly: EntryPointModule(typeof(BootstrapSelector))]
 
-namespace Microsoft.ML.Ensemble.Selector.SubsetSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubsetSelector
 {
     internal sealed class BootstrapSelector : BaseSubsetSelector<BootstrapSelector.Arguments>
     {
diff --git a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/RandomPartitionSelector.cs b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/RandomPartitionSelector.cs
index a142189808..a7f52e6b7b 100644
--- a/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/RandomPartitionSelector.cs
+++ b/src/Microsoft.ML.Ensemble/Selector/SubsetSelector/RandomPartitionSelector.cs
@@ -6,9 +6,9 @@
 using System.Collections.Generic;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubsetSelector;
 using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Trainers.Ensemble;
+using Microsoft.ML.Trainers.Ensemble.SubsetSelector;
 using Microsoft.ML.Transforms;
 
 [assembly: LoadableClass(typeof(RandomPartitionSelector), typeof(RandomPartitionSelector.Arguments),
@@ -16,7 +16,7 @@
 
 [assembly: EntryPointModule(typeof(RandomPartitionSelector))]
 
-namespace Microsoft.ML.Ensemble.Selector.SubsetSelector
+namespace Microsoft.ML.Trainers.Ensemble.SubsetSelector
 {
     internal sealed class RandomPartitionSelector : BaseSubsetSelector<RandomPartitionSelector.Arguments>
     {
diff --git a/src/Microsoft.ML.Ensemble/Subset.cs b/src/Microsoft.ML.Ensemble/Subset.cs
index 743be33df6..e1e579fd7b 100644
--- a/src/Microsoft.ML.Ensemble/Subset.cs
+++ b/src/Microsoft.ML.Ensemble/Subset.cs
@@ -5,7 +5,7 @@
 using System.Collections;
 using Microsoft.ML.Data;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     internal sealed class Subset
     {
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
index d63a183f2c..1e734c875c 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Binary/EnsembleTrainer.cs
@@ -7,11 +7,8 @@
 using System.Linq;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
-using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
 
@@ -22,7 +19,7 @@
 [assembly: LoadableClass(typeof(EnsembleTrainer), typeof(EnsembleTrainer.Arguments), typeof(SignatureModelCombiner),
     "Binary Classification Ensemble Model Combiner", EnsembleTrainer.LoadNameValue, "pe", "ParallelEnsemble")]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TDistPredictor = IDistPredictorProducing<Single, Single>;
     using TScalarPredictor = IPredictorProducing<Single>;
diff --git a/src/Microsoft.ML.Ensemble/Trainer/EnsembleDistributionModelParameters.cs b/src/Microsoft.ML.Ensemble/Trainer/EnsembleDistributionModelParameters.cs
index e63f2059e5..d425db8e4f 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/EnsembleDistributionModelParameters.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/EnsembleDistributionModelParameters.cs
@@ -9,16 +9,15 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 // These are for deserialization from a model repository.
 [assembly: LoadableClass(typeof(EnsembleDistributionModelParameters), null, typeof(SignatureLoadModel),
     EnsembleDistributionModelParameters.UserName, EnsembleDistributionModelParameters.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TDistPredictor = IDistPredictorProducing<Single, Single>;
 
diff --git a/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParameters.cs b/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParameters.cs
index 032ff39695..9eb56d5bfd 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParameters.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParameters.cs
@@ -7,17 +7,16 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(EnsembleModelParameters), null, typeof(SignatureLoadModel), EnsembleModelParameters.UserName,
     EnsembleModelParameters.LoaderSignature)]
 
 [assembly: EntryPointModule(typeof(EnsembleModelParameters))]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     /// <summary>
     /// A class for artifacts of ensembled models.
diff --git a/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParametersBase.cs b/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParametersBase.cs
index 5e79bbe41e..f8568cc2d7 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParametersBase.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/EnsembleModelParametersBase.cs
@@ -6,12 +6,12 @@
 using System.Collections.Generic;
 using System.IO;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public abstract class EnsembleModelParametersBase<TOutput> : ModelParametersBase<TOutput>,
         IPredictorProducing<TOutput>, ICanSaveInTextFormat, ICanSaveSummary
diff --git a/src/Microsoft.ML.Ensemble/Trainer/EnsembleTrainerBase.cs b/src/Microsoft.ML.Ensemble/Trainer/EnsembleTrainerBase.cs
index ef7ebc6d4d..b9aad6228f 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/EnsembleTrainerBase.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/EnsembleTrainerBase.cs
@@ -8,15 +8,13 @@
 using System.Threading.Tasks;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble.OutputCombiners;
-using Microsoft.ML.Ensemble.Selector;
-using Microsoft.ML.Ensemble.Selector.SubsetSelector;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Internallearn;
 using Microsoft.ML.Internal.Utilities;
+using Microsoft.ML.Trainers.Ensemble.SubsetSelector;
 using Microsoft.ML.Training;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using Stopwatch = System.Diagnostics.Stopwatch;
 
diff --git a/src/Microsoft.ML.Ensemble/Trainer/IModelCombiner.cs b/src/Microsoft.ML.Ensemble/Trainer/IModelCombiner.cs
index 85f55d8111..48af1cfcfa 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/IModelCombiner.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/IModelCombiner.cs
@@ -4,7 +4,7 @@
 
 using System.Collections.Generic;
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     public delegate void SignatureModelCombiner(PredictionKind kind);
 
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/EnsembleMultiClassModelParameters.cs b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/EnsembleMultiClassModelParameters.cs
index 134e58d51d..163b256f1e 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/EnsembleMultiClassModelParameters.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/EnsembleMultiClassModelParameters.cs
@@ -7,17 +7,14 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.Model;
+using Microsoft.ML.Trainers.Ensemble;
 
 [assembly: LoadableClass(typeof(EnsembleMultiClassModelParameters), null, typeof(SignatureLoadModel),
     EnsembleMultiClassModelParameters.UserName, EnsembleMultiClassModelParameters.LoaderSignature)]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
-    using TVectorPredictor = IPredictorProducing<VBuffer<Single>>;
-
     public sealed class EnsembleMultiClassModelParameters : EnsembleModelParametersBase<VBuffer<Single>>, IValueMapper
     {
         internal const string UserName = "Ensemble Multiclass Executor";
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
index 9f5446d327..4420b15ba9 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Multiclass/MulticlassDataPartitionEnsembleTrainer.cs
@@ -8,11 +8,8 @@
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
-using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Training;
 
 [assembly: LoadableClass(MulticlassDataPartitionEnsembleTrainer.Summary, typeof(MulticlassDataPartitionEnsembleTrainer),
@@ -24,7 +21,7 @@
 [assembly: LoadableClass(typeof(MulticlassDataPartitionEnsembleTrainer), typeof(MulticlassDataPartitionEnsembleTrainer.Arguments),
     typeof(SignatureModelCombiner), "Multiclass Classification Ensemble Model Combiner", MulticlassDataPartitionEnsembleTrainer.LoadNameValue)]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TVectorPredictor = IPredictorProducing<VBuffer<Single>>;
     /// <summary>
diff --git a/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs b/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
index a4075c9c94..e3b7976d51 100644
--- a/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
+++ b/src/Microsoft.ML.Ensemble/Trainer/Regression/RegressionEnsembleTrainer.cs
@@ -7,11 +7,8 @@
 using System.Linq;
 using Microsoft.ML;
 using Microsoft.ML.CommandLine;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
-using Microsoft.ML.Ensemble.Selector;
 using Microsoft.ML.Internal.Internallearn;
-using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Trainers.Online;
 using Microsoft.ML.Training;
 
@@ -23,7 +20,7 @@
 [assembly: LoadableClass(typeof(RegressionEnsembleTrainer), typeof(RegressionEnsembleTrainer.Arguments), typeof(SignatureModelCombiner),
     "Regression Ensemble Model Combiner", RegressionEnsembleTrainer.LoadNameValue)]
 
-namespace Microsoft.ML.Ensemble
+namespace Microsoft.ML.Trainers.Ensemble
 {
     using TScalarPredictor = IPredictorProducing<Single>;
     internal sealed class RegressionEnsembleTrainer : EnsembleTrainerBase<Single, TScalarPredictor,
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
index 48819147ef..ab451165c8 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/TreeEnsembleCombiner.cs
@@ -4,10 +4,9 @@
 
 using System.Collections.Generic;
 using Microsoft.ML;
-using Microsoft.ML.Calibrator;
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
 using Microsoft.ML.Internal.Calibration;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Trainers.FastTree;
 
 [assembly: LoadableClass(typeof(TreeEnsembleCombiner), null, typeof(SignatureModelCombiner), "Fast Tree Model Combiner", "FastTreeCombiner")]
diff --git a/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs b/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
index 85398dded2..8eec3a20e3 100644
--- a/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
+++ b/src/Microsoft.ML.TimeSeries.StaticPipe/TimeSeriesStatic.cs
@@ -5,7 +5,7 @@
 using System.Collections.Generic;
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.StaticPipe.Runtime;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML.StaticPipe
 {
diff --git a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
index f41298e41f..cf5e602cf9 100644
--- a/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
+++ b/src/Microsoft.ML.TimeSeries/AdaptiveSingularSpectrumSequenceModeler.cs
@@ -11,14 +11,13 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(typeof(AdaptiveSingularSpectrumSequenceModeler), typeof(AdaptiveSingularSpectrumSequenceModeler), null, typeof(SignatureLoadModel),
     "SSA Sequence Modeler",
     AdaptiveSingularSpectrumSequenceModeler.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class implements basic Singular Spectrum Analysis (SSA) model for modeling univariate time-series.
diff --git a/src/Microsoft.ML.TimeSeries/EigenUtils.cs b/src/Microsoft.ML.TimeSeries/EigenUtils.cs
index 98be26f316..fc8224b94e 100644
--- a/src/Microsoft.ML.TimeSeries/EigenUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/EigenUtils.cs
@@ -8,7 +8,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Float = System.Single;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     //REVIEW: improve perf with SSE and Multithreading
     internal static class EigenUtils
diff --git a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
index dae1fae4f5..29653b2620 100644
--- a/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/ExponentialAverageTransform.cs
@@ -10,14 +10,14 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(ExponentialAverageTransform.Summary, typeof(ExponentialAverageTransform), typeof(ExponentialAverageTransform.Arguments), typeof(SignatureDataTransform),
     ExponentialAverageTransform.UserName, ExponentialAverageTransform.LoaderSignature, ExponentialAverageTransform.ShortName)]
 [assembly: LoadableClass(ExponentialAverageTransform.Summary, typeof(ExponentialAverageTransform), null, typeof(SignatureLoadDataTransform),
     ExponentialAverageTransform.UserName, ExponentialAverageTransform.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// ExponentialAverageTransform is a weighted average of the values: ExpAvg(y_t) = a * y_t + (1-a) * ExpAvg(y_(t-1)).
diff --git a/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs b/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs
index 4090a26ebb..e013f7c671 100644
--- a/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs
+++ b/src/Microsoft.ML.TimeSeries/ExtensionsCatalog.cs
@@ -3,7 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML.Data;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 namespace Microsoft.ML
 {
diff --git a/src/Microsoft.ML.TimeSeries/FftUtils.cs b/src/Microsoft.ML.TimeSeries/FftUtils.cs
index a02575c031..28a19a89ed 100644
--- a/src/Microsoft.ML.TimeSeries/FftUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/FftUtils.cs
@@ -6,7 +6,7 @@
 using System.Runtime.InteropServices;
 using System.Security;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// The utility functions that wrap the native Discrete Fast Fourier Transform functionality from Intel MKL.
diff --git a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
index 4773fac099..25cf99347b 100644
--- a/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/IidAnomalyDetectionBase.cs
@@ -9,9 +9,8 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// The is the wrapper to <see cref="IidAnomalyDetectionBase"/> that computes the p-values and martingale scores for a supposedly i.i.d input sequence of floats. In other words, it assumes
diff --git a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
index 3984d6973d..9f5fc85a2d 100644
--- a/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidChangePointDetector.cs
@@ -12,8 +12,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(IidChangePointDetector.Summary, typeof(IDataTransform), typeof(IidChangePointDetector), typeof(IidChangePointDetector.Options), typeof(SignatureDataTransform),
     IidChangePointDetector.UserName, IidChangePointDetector.LoaderSignature, IidChangePointDetector.ShortName)]
@@ -27,7 +26,7 @@
 [assembly: LoadableClass(typeof(IRowMapper), typeof(IidChangePointDetector), null, typeof(SignatureLoadRowMapper),
    IidChangePointDetector.UserName, IidChangePointDetector.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class implements the change point detector transform for an i.i.d. sequence based on adaptive kernel density estimation and martingales.
diff --git a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
index c6d06e66ae..813043606a 100644
--- a/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs
@@ -11,8 +11,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(IidSpikeDetector.Summary, typeof(IDataTransform), typeof(IidSpikeDetector), typeof(IidSpikeDetector.Options), typeof(SignatureDataTransform),
     IidSpikeDetector.UserName, IidSpikeDetector.LoaderSignature, IidSpikeDetector.ShortName)]
@@ -26,7 +25,7 @@
 [assembly: LoadableClass(typeof(IRowMapper), typeof(IidSpikeDetector), null, typeof(SignatureLoadRowMapper),
    IidSpikeDetector.UserName, IidSpikeDetector.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class implements the spike detector transform for an i.i.d. sequence based on adaptive kernel density estimation.
diff --git a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
index 2c51fa13f5..1e04173d49 100644
--- a/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/MovingAverageTransform.cs
@@ -10,14 +10,14 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(MovingAverageTransform.Summary, typeof(MovingAverageTransform), typeof(MovingAverageTransform.Arguments), typeof(SignatureDataTransform),
     "Moving Average Transform", MovingAverageTransform.LoaderSignature, "MoAv")]
 [assembly: LoadableClass(MovingAverageTransform.Summary, typeof(MovingAverageTransform), null, typeof(SignatureLoadDataTransform),
     "Moving Average Transform", MovingAverageTransform.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// MovingAverageTransform is a weighted average of the values in
diff --git a/src/Microsoft.ML.TimeSeries/PValueTransform.cs b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
index a1723ef162..528f8b7fd0 100644
--- a/src/Microsoft.ML.TimeSeries/PValueTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PValueTransform.cs
@@ -10,14 +10,14 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(PValueTransform.Summary, typeof(PValueTransform), typeof(PValueTransform.Arguments), typeof(SignatureDataTransform),
     PValueTransform.UserName, PValueTransform.LoaderSignature, PValueTransform.ShortName)]
 [assembly: LoadableClass(PValueTransform.Summary, typeof(PValueTransform), null, typeof(SignatureLoadDataTransform),
     PValueTransform.UserName, PValueTransform.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// PValueTransform is a sequential transform that computes the empirical p-value of the current value in the series based on the other values in
diff --git a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
index bfac8ad93a..a4e8c792d0 100644
--- a/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/PercentileThresholdTransform.cs
@@ -10,14 +10,14 @@
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(PercentileThresholdTransform.Summary, typeof(PercentileThresholdTransform), typeof(PercentileThresholdTransform.Arguments), typeof(SignatureDataTransform),
     PercentileThresholdTransform.UserName, PercentileThresholdTransform.LoaderSignature, PercentileThresholdTransform.ShortName)]
 [assembly: LoadableClass(PercentileThresholdTransform.Summary, typeof(PercentileThresholdTransform), null, typeof(SignatureLoadDataTransform),
     PercentileThresholdTransform.UserName, PercentileThresholdTransform.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// PercentileThresholdTransform is a sequential transform that decides whether the current value of the time-series belongs to the 'percentile' % of the top values in
diff --git a/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs b/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
index 19b1d7f97d..7ba7dd0d25 100644
--- a/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/PolynomialUtils.cs
@@ -8,7 +8,7 @@
 using System.Numerics;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     internal static class PolynomialUtils
     {
diff --git a/src/Microsoft.ML.TimeSeries/PredictionFunction.cs b/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
index 0e97713247..5aab239fef 100644
--- a/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
+++ b/src/Microsoft.ML.TimeSeries/PredictionFunction.cs
@@ -10,7 +10,7 @@
 using Microsoft.ML.Core.Data;
 using Microsoft.ML.Data;
 
-namespace Microsoft.ML.TimeSeries
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     internal interface IStatefulRowToRowMapper : IRowToRowMapper
     {
diff --git a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
index d643435704..b6da0925cc 100644
--- a/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequenceModelerBase.cs
@@ -6,7 +6,7 @@
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// The base container class for the forecast result on a sequence of type <typeparamref name="T"/>.
diff --git a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
index d9c3750cd6..4bd1cbc87d 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs
@@ -12,9 +12,9 @@
 using Microsoft.ML.Internal.CpuMath;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
+using Microsoft.ML.Transforms.TimeSeries;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// The type of the martingale.
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
index d476ba6c64..728d3bc710 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformBase.cs
@@ -11,7 +11,7 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Transforms;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// The box class that is used to box the TInput and TOutput for the LambdaTransform.
diff --git a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
index e0878e58e1..1c795b1bee 100644
--- a/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SequentialTransformerBase.cs
@@ -13,10 +13,8 @@
 using Microsoft.ML.Model;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.Model.Pfa;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.Transforms;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
 
     /// <summary>
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
index 3f57b81113..103d63f0fa 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransform.cs
@@ -7,14 +7,14 @@
 using Microsoft.ML;
 using Microsoft.ML.Data;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(SlidingWindowTransform.Summary, typeof(SlidingWindowTransform), typeof(SlidingWindowTransform.Arguments), typeof(SignatureDataTransform),
     SlidingWindowTransform.UserName, SlidingWindowTransform.LoaderSignature, SlidingWindowTransform.ShortName)]
 [assembly: LoadableClass(SlidingWindowTransform.Summary, typeof(SlidingWindowTransform), null, typeof(SignatureLoadDataTransform),
     SlidingWindowTransform.UserName, SlidingWindowTransform.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// Outputs a sliding window on a time series of type Single.
diff --git a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
index 84abfacc94..677ddc2488 100644
--- a/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SlidingWindowTransformBase.cs
@@ -6,13 +6,11 @@
 using Microsoft.Data.DataView;
 using Microsoft.ML.CommandLine;
 using Microsoft.ML.Data;
-using Microsoft.ML.Data.Conversion;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.Transforms;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// SlidingWindowTransformBase outputs a sliding window as a VBuffer from a series of any type.
@@ -104,7 +102,7 @@ private TInput GetNaValue()
             int index;
             sch.TryGetColumnIndex(InputColumnName, out index);
             ColumnType col = sch[index].Type;
-            TInput nanValue = Conversions.Instance.GetNAOrDefault<TInput>(col);
+            TInput nanValue = Data.Conversion.Conversions.Instance.GetNAOrDefault<TInput>(col);
 
             // We store the nan_value here to avoid getting it each time a state is instanciated.
             return nanValue;
diff --git a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
index 4cb858485a..3a7411693f 100644
--- a/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaAnomalyDetectionBase.cs
@@ -10,9 +10,9 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.Internal.Utilities;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
+using Microsoft.ML.Transforms.TimeSeries;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     public enum ErrorFunction : byte
     {
diff --git a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
index 6da61935dc..e6ea241e26 100644
--- a/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaChangePointDetector.cs
@@ -12,8 +12,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(SsaChangePointDetector.Summary, typeof(IDataTransform), typeof(SsaChangePointDetector), typeof(SsaChangePointDetector.Options), typeof(SignatureDataTransform),
     SsaChangePointDetector.UserName, SsaChangePointDetector.LoaderSignature, SsaChangePointDetector.ShortName)]
@@ -27,7 +26,7 @@
 [assembly: LoadableClass(typeof(IRowMapper), typeof(SsaChangePointDetector), null, typeof(SignatureLoadRowMapper),
    SsaChangePointDetector.UserName, SsaChangePointDetector.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class implements the change point detector transform based on Singular Spectrum modeling of the time-series.
diff --git a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
index 9a2ad25aab..399bc6b6e0 100644
--- a/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
+++ b/src/Microsoft.ML.TimeSeries/SsaSpikeDetector.cs
@@ -11,8 +11,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Model;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: LoadableClass(SsaSpikeDetector.Summary, typeof(IDataTransform), typeof(SsaSpikeDetector), typeof(SsaSpikeDetector.Options), typeof(SignatureDataTransform),
     SsaSpikeDetector.UserName, SsaSpikeDetector.LoaderSignature, SsaSpikeDetector.ShortName)]
@@ -26,7 +25,7 @@
 [assembly: LoadableClass(typeof(IRowMapper), typeof(SsaSpikeDetector), null, typeof(SignatureLoadRowMapper),
    SsaSpikeDetector.UserName, SsaSpikeDetector.LoaderSignature)]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class implements the spike detector transform based on Singular Spectrum modeling of the time-series.
diff --git a/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs b/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
index da5c7b9153..e128a4730c 100644
--- a/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
+++ b/src/Microsoft.ML.TimeSeries/TimeSeriesProcessing.cs
@@ -3,11 +3,11 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML.EntryPoints;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 
 [assembly: EntryPointModule(typeof(TimeSeriesProcessingEntryPoints))]
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// Entry points for text anylytics transforms.
@@ -26,7 +26,9 @@ internal static CommonOutputs.TransformOutput ExponentialAverage(IHostEnvironmen
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.IidChangePointDetector.Summary, UserName = TimeSeriesProcessing.IidChangePointDetector.UserName, ShortName = TimeSeriesProcessing.IidChangePointDetector.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.IidChangePointDetector.Summary,
+            UserName = TimeSeries.IidChangePointDetector.UserName,
+            ShortName = TimeSeries.IidChangePointDetector.ShortName)]
         internal static CommonOutputs.TransformOutput IidChangePointDetector(IHostEnvironment env, IidChangePointDetector.Options options)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidChangePointDetector", options);
@@ -38,7 +40,9 @@ internal static CommonOutputs.TransformOutput IidChangePointDetector(IHostEnviro
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.IidSpikeDetector.Summary, UserName = TimeSeriesProcessing.IidSpikeDetector.UserName, ShortName = TimeSeriesProcessing.IidSpikeDetector.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.IidSpikeDetector.Summary,
+            UserName = TimeSeries.IidSpikeDetector.UserName,
+            ShortName = TimeSeries.IidSpikeDetector.ShortName)]
         internal static CommonOutputs.TransformOutput IidSpikeDetector(IHostEnvironment env, IidSpikeDetector.Options options)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "IidSpikeDetector", options);
@@ -50,7 +54,9 @@ internal static CommonOutputs.TransformOutput IidSpikeDetector(IHostEnvironment
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.PercentileThresholdTransform.Summary, UserName = TimeSeriesProcessing.PercentileThresholdTransform.UserName, ShortName = TimeSeriesProcessing.PercentileThresholdTransform.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.PercentileThresholdTransform.Summary,
+            UserName = TimeSeries.PercentileThresholdTransform.UserName,
+            ShortName = TimeSeries.PercentileThresholdTransform.ShortName)]
         internal static CommonOutputs.TransformOutput PercentileThresholdTransform(IHostEnvironment env, PercentileThresholdTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "PercentileThresholdTransform", input);
@@ -62,7 +68,9 @@ internal static CommonOutputs.TransformOutput PercentileThresholdTransform(IHost
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.PValueTransform.Summary, UserName = TimeSeriesProcessing.PValueTransform.UserName, ShortName = TimeSeriesProcessing.PValueTransform.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.PValueTransform.Summary,
+            UserName = TimeSeries.PValueTransform.UserName,
+            ShortName = TimeSeries.PValueTransform.ShortName)]
         internal static CommonOutputs.TransformOutput PValueTransform(IHostEnvironment env, PValueTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "PValueTransform", input);
@@ -74,7 +82,9 @@ internal static CommonOutputs.TransformOutput PValueTransform(IHostEnvironment e
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SlidingWindowTransform.Summary, UserName = TimeSeriesProcessing.SlidingWindowTransform.UserName, ShortName = TimeSeriesProcessing.SlidingWindowTransform.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.SlidingWindowTransform.Summary,
+            UserName = TimeSeries.SlidingWindowTransform.UserName,
+            ShortName = TimeSeries.SlidingWindowTransform.ShortName)]
         internal static CommonOutputs.TransformOutput SlidingWindowTransform(IHostEnvironment env, SlidingWindowTransform.Arguments input)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SlidingWindowTransform", input);
@@ -86,7 +96,9 @@ internal static CommonOutputs.TransformOutput SlidingWindowTransform(IHostEnviro
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SsaChangePointDetector.Summary, UserName = TimeSeriesProcessing.SsaChangePointDetector.UserName, ShortName = TimeSeriesProcessing.SsaChangePointDetector.ShortName)]
+        [TlcModule.EntryPoint(Desc = TimeSeries.SsaChangePointDetector.Summary,
+            UserName = TimeSeries.SsaChangePointDetector.UserName,
+            ShortName = TimeSeries.SsaChangePointDetector.ShortName)]
         internal static CommonOutputs.TransformOutput SsaChangePointDetector(IHostEnvironment env, SsaChangePointDetector.Options options)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaChangePointDetector", options);
@@ -98,8 +110,10 @@ internal static CommonOutputs.TransformOutput SsaChangePointDetector(IHostEnviro
             };
         }
 
-        [TlcModule.EntryPoint(Desc = TimeSeriesProcessing.SsaSpikeDetector.Summary, UserName = TimeSeriesProcessing.SsaSpikeDetector.UserName, ShortName = TimeSeriesProcessing.SsaSpikeDetector.ShortName)]
-        internal static CommonOutputs.TransformOutput SsaSpikeDetector(IHostEnvironment env, SsaSpikeDetector.Options options)
+        [TlcModule.EntryPoint(Desc = TimeSeries.SsaSpikeDetector.Summary,
+            UserName = TimeSeries.SsaSpikeDetector.UserName,
+            ShortName = TimeSeries.SsaSpikeDetector.ShortName)]
+        public static CommonOutputs.TransformOutput SsaSpikeDetector(IHostEnvironment env, SsaSpikeDetector.Options options)
         {
             var h = EntryPointUtils.CheckArgsAndCreateHost(env, "SsaSpikeDetector", options);
             var view = new SsaSpikeEstimator(h, options).Fit(options.Data).Transform(options.Data);
diff --git a/src/Microsoft.ML.TimeSeries/TimeSeriesUtils.cs b/src/Microsoft.ML.TimeSeries/TimeSeriesUtils.cs
index 82334f7976..e8eb79f1ba 100644
--- a/src/Microsoft.ML.TimeSeries/TimeSeriesUtils.cs
+++ b/src/Microsoft.ML.TimeSeries/TimeSeriesUtils.cs
@@ -2,7 +2,7 @@
 using System.IO;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.TimeSeries
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     internal static class TimeSeriesUtils
     {
diff --git a/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs b/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
index b8153cd03f..2ea67dae96 100644
--- a/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
+++ b/src/Microsoft.ML.TimeSeries/TrajectoryMatrix.cs
@@ -5,7 +5,7 @@
 using System;
 using Microsoft.ML.Internal.Utilities;
 
-namespace Microsoft.ML.TimeSeriesProcessing
+namespace Microsoft.ML.Transforms.TimeSeries
 {
     /// <summary>
     /// This class encapsulates the trajectory matrix of a time-series used in Singular Spectrum Analysis (SSA).
diff --git a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
index d1a8e3c139..0c60d474be 100644
--- a/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
+++ b/test/BaselineOutput/Common/EntryPoints/core_ep-list.tsv
@@ -4,19 +4,19 @@ Data.IDataViewArrayConverter	Create an array variable of IDataView	Microsoft.ML.
 Data.PredictorModelArrayConverter	Create an array variable of PredictorModel	Microsoft.ML.EntryPoints.MacroUtils	MakeArray	Microsoft.ML.EntryPoints.MacroUtils+ArrayIPredictorModelInput	Microsoft.ML.EntryPoints.MacroUtils+ArrayIPredictorModelOutput
 Data.TextLoader	Import a dataset from a text file	Microsoft.ML.EntryPoints.ImportTextData	TextLoader	Microsoft.ML.EntryPoints.ImportTextData+LoaderInput	Microsoft.ML.EntryPoints.ImportTextData+Output
 Models.AnomalyDetectionEvaluator	Evaluates an anomaly detection scored dataset.	Microsoft.ML.Data.Evaluate	AnomalyDetection	Microsoft.ML.Data.AnomalyDetectionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.AnomalyPipelineEnsemble	Combine anomaly detection models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateAnomalyPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineAnomalyInput	Microsoft.ML.EntryPoints.CommonOutputs+AnomalyDetectionOutput
+Models.AnomalyPipelineEnsemble	Combine anomaly detection models into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateAnomalyPipelineEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+PipelineAnomalyInput	Microsoft.ML.EntryPoints.CommonOutputs+AnomalyDetectionOutput
 Models.BinaryClassificationEvaluator	Evaluates a binary classification scored dataset.	Microsoft.ML.Data.Evaluate	Binary	Microsoft.ML.Data.BinaryClassifierMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+ClassificationEvaluateOutput
-Models.BinaryEnsemble	Combine binary classifiers into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+ClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Models.BinaryPipelineEnsemble	Combine binary classification models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateBinaryPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Models.BinaryEnsemble	Combine binary classifiers into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateBinaryEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+ClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Models.BinaryPipelineEnsemble	Combine binary classification models into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateBinaryPipelineEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Models.ClassificationEvaluator	Evaluates a multi class classification scored dataset.	Microsoft.ML.Data.Evaluate	MultiClass	Microsoft.ML.Data.MultiClassMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+ClassificationEvaluateOutput
 Models.ClusterEvaluator	Evaluates a clustering scored dataset.	Microsoft.ML.Data.Evaluate	Clustering	Microsoft.ML.Data.ClusteringMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.CrossValidationResultsCombiner	Combine the metric data views returned from cross validation.	Microsoft.ML.EntryPoints.CrossValidationMacro	CombineMetrics	Microsoft.ML.EntryPoints.CrossValidationMacro+CombineMetricsInput	Microsoft.ML.EntryPoints.CrossValidationMacro+CombinedOutput
 Models.CrossValidator	Cross validation for general learning	Microsoft.ML.EntryPoints.CrossValidationMacro	CrossValidate	Microsoft.ML.EntryPoints.CrossValidationMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.CrossValidationMacro+Output]
 Models.CrossValidatorDatasetSplitter	Split the dataset into the specified number of cross-validation folds (train and test sets)	Microsoft.ML.EntryPoints.CVSplit	Split	Microsoft.ML.EntryPoints.CVSplit+Input	Microsoft.ML.EntryPoints.CVSplit+Output
 Models.DatasetTransformer	Applies a TransformModel to a dataset.	Microsoft.ML.EntryPoints.ModelOperations	Apply	Microsoft.ML.EntryPoints.ModelOperations+ApplyTransformModelInput	Microsoft.ML.EntryPoints.ModelOperations+ApplyTransformModelOutput
-Models.EnsembleSummary	Summarize a pipeline ensemble predictor.	Microsoft.ML.Ensemble.PipelineEnsemble	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.Ensemble.PipelineEnsemble+SummaryOutput
+Models.EnsembleSummary	Summarize a pipeline ensemble predictor.	Microsoft.ML.Trainers.Ensemble.PipelineEnsemble	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.Trainers.Ensemble.PipelineEnsemble+SummaryOutput
 Models.FixedPlattCalibrator	Apply a Platt calibrator with a fixed slope and offset to an input model	Microsoft.ML.Internal.Calibration.Calibrate	FixedPlatt	Microsoft.ML.Internal.Calibration.Calibrate+FixedPlattInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
-Models.MultiClassPipelineEnsemble	Combine multiclass classifiers into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateMultiClassPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
+Models.MultiClassPipelineEnsemble	Combine multiclass classifiers into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateMultiClassPipelineEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+PipelineClassifierInput	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
 Models.MultiOutputRegressionEvaluator	Evaluates a multi output regression scored dataset.	Microsoft.ML.Data.Evaluate	MultiOutputRegression	Microsoft.ML.Data.MultiOutputRegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.NaiveCalibrator	Apply a Naive calibrator to an input model	Microsoft.ML.Internal.Calibration.Calibrate	Naive	Microsoft.ML.Internal.Calibration.Calibrate+NoArgumentsInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
 Models.OneVersusAll	One-vs-All macro (OVA)	Microsoft.ML.EntryPoints.OneVersusAllMacro	OneVersusAll	Microsoft.ML.EntryPoints.OneVersusAllMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.OneVersusAllMacro+Output]
@@ -26,23 +26,23 @@ Models.PAVCalibrator	Apply a PAV calibrator to an input model	Microsoft.ML.Inter
 Models.PlattCalibrator	Apply a Platt calibrator to an input model	Microsoft.ML.Internal.Calibration.Calibrate	Platt	Microsoft.ML.Internal.Calibration.Calibrate+NoArgumentsInput	Microsoft.ML.EntryPoints.CommonOutputs+CalibratorOutput
 Models.QuantileRegressionEvaluator	Evaluates a quantile regression scored dataset.	Microsoft.ML.Data.Evaluate	QuantileRegression	Microsoft.ML.Data.QuantileRegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
 Models.RankerEvaluator	Evaluates a ranking scored dataset.	Microsoft.ML.Data.Evaluate	Ranking	Microsoft.ML.Data.RankerMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.RegressionEnsemble	Combine regression models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateRegressionEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+RegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Models.RegressionEnsemble	Combine regression models into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateRegressionEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+RegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Models.RegressionEvaluator	Evaluates a regression scored dataset.	Microsoft.ML.Data.Evaluate	Regression	Microsoft.ML.Data.RegressionMamlEvaluator+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+CommonEvaluateOutput
-Models.RegressionPipelineEnsemble	Combine regression models into an ensemble	Microsoft.ML.Ensemble.EnsembleCreator	CreateRegressionPipelineEnsemble	Microsoft.ML.Ensemble.EnsembleCreator+PipelineRegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Models.RegressionPipelineEnsemble	Combine regression models into an ensemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator	CreateRegressionPipelineEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleCreator+PipelineRegressionInput	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Models.Summarizer	Summarize a linear regression predictor.	Microsoft.ML.EntryPoints.SummarizePredictor	Summarize	Microsoft.ML.EntryPoints.SummarizePredictor+Input	Microsoft.ML.EntryPoints.CommonOutputs+SummaryOutput
 Models.TrainTestEvaluator	General train test for any supported evaluator	Microsoft.ML.EntryPoints.TrainTestMacro	TrainTest	Microsoft.ML.EntryPoints.TrainTestMacro+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MacroOutput`1[Microsoft.ML.EntryPoints.TrainTestMacro+Output]
-TimeSeriesProcessingEntryPoints.ExponentialAverage	Applies a Exponential average on a time series.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	ExponentialAverage	Microsoft.ML.TimeSeriesProcessing.ExponentialAverageTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.IidChangePointDetector	This transform detects the change-points in an i.i.d. sequence using adaptive kernel density estimation and martingales.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidChangePointDetector	Microsoft.ML.TimeSeriesProcessing.IidChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.IidSpikeDetector	This transform detects the spikes in a i.i.d. sequence using adaptive kernel density estimation.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	IidSpikeDetector	Microsoft.ML.TimeSeriesProcessing.IidSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.PercentileThresholdTransform	Detects the values of time-series that are in the top percentile of the sliding window.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	PercentileThresholdTransform	Microsoft.ML.TimeSeriesProcessing.PercentileThresholdTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.PValueTransform	This P-Value transform calculates the p-value of the current input in the sequence with regard to the values in the sliding window.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	PValueTransform	Microsoft.ML.TimeSeriesProcessing.PValueTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.SlidingWindowTransform	Returns the last values for a time series [y(t-d-l+1), y(t-d-l+2), ..., y(t-l-1), y(t-l)] where d is the size of the window, l the lag and y is a Float.	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SlidingWindowTransform	Microsoft.ML.TimeSeriesProcessing.SlidingWindowTransformBase`1+Arguments[System.Single]	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.SsaChangePointDetector	This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaChangePointDetector	Microsoft.ML.TimeSeriesProcessing.SsaChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
-TimeSeriesProcessingEntryPoints.SsaSpikeDetector	This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.TimeSeriesProcessing.TimeSeriesProcessingEntryPoints	SsaSpikeDetector	Microsoft.ML.TimeSeriesProcessing.SsaSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.ExponentialAverage	Applies a Exponential average on a time series.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	ExponentialAverage	Microsoft.ML.Transforms.TimeSeries.ExponentialAverageTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.IidChangePointDetector	This transform detects the change-points in an i.i.d. sequence using adaptive kernel density estimation and martingales.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	IidChangePointDetector	Microsoft.ML.Transforms.TimeSeries.IidChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.IidSpikeDetector	This transform detects the spikes in a i.i.d. sequence using adaptive kernel density estimation.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	IidSpikeDetector	Microsoft.ML.Transforms.TimeSeries.IidSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.PercentileThresholdTransform	Detects the values of time-series that are in the top percentile of the sliding window.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	PercentileThresholdTransform	Microsoft.ML.Transforms.TimeSeries.PercentileThresholdTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.PValueTransform	This P-Value transform calculates the p-value of the current input in the sequence with regard to the values in the sliding window.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	PValueTransform	Microsoft.ML.Transforms.TimeSeries.PValueTransform+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.SlidingWindowTransform	Returns the last values for a time series [y(t-d-l+1), y(t-d-l+2), ..., y(t-l-1), y(t-l)] where d is the size of the window, l the lag and y is a Float.	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	SlidingWindowTransform	Microsoft.ML.Transforms.TimeSeries.SlidingWindowTransformBase`1+Arguments[System.Single]	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.SsaChangePointDetector	This transform detects the change-points in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	SsaChangePointDetector	Microsoft.ML.Transforms.TimeSeries.SsaChangePointDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
+TimeSeriesProcessingEntryPoints.SsaSpikeDetector	This transform detects the spikes in a seasonal time-series using Singular Spectrum Analysis (SSA).	Microsoft.ML.Transforms.TimeSeries.TimeSeriesProcessingEntryPoints	SsaSpikeDetector	Microsoft.ML.Transforms.TimeSeries.SsaSpikeDetector+Options	Microsoft.ML.EntryPoints.CommonOutputs+TransformOutput
 Trainers.AveragedPerceptronBinaryClassifier	Averaged Perceptron Binary Classifier.	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer	TrainBinary	Microsoft.ML.Trainers.Online.AveragedPerceptronTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.EnsembleBinaryClassifier	Train binary ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateBinaryEnsemble	Microsoft.ML.Ensemble.EnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
-Trainers.EnsembleClassification	Train multiclass ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateMultiClassEnsemble	Microsoft.ML.Ensemble.MulticlassDataPartitionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
-Trainers.EnsembleRegression	Train regression ensemble.	Microsoft.ML.Ensemble.Ensemble	CreateRegressionEnsemble	Microsoft.ML.Ensemble.RegressionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
+Trainers.EnsembleBinaryClassifier	Train binary ensemble.	Microsoft.ML.Trainers.Ensemble.Ensemble	CreateBinaryEnsemble	Microsoft.ML.Trainers.Ensemble.EnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
+Trainers.EnsembleClassification	Train multiclass ensemble.	Microsoft.ML.Trainers.Ensemble.Ensemble	CreateMultiClassEnsemble	Microsoft.ML.Trainers.Ensemble.MulticlassDataPartitionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+MulticlassClassificationOutput
+Trainers.EnsembleRegression	Train regression ensemble.	Microsoft.ML.Trainers.Ensemble.Ensemble	CreateRegressionEnsemble	Microsoft.ML.Trainers.Ensemble.RegressionEnsembleTrainer+Arguments	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.FastForestBinaryClassifier	Uses a random forest learner to perform binary classification.	Microsoft.ML.Trainers.FastTree.FastForest	TrainBinary	Microsoft.ML.Trainers.FastTree.FastForestClassification+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
 Trainers.FastForestRegressor	Trains a random forest to fit target values using least-squares.	Microsoft.ML.Trainers.FastTree.FastForest	TrainRegression	Microsoft.ML.Trainers.FastTree.FastForestRegression+Options	Microsoft.ML.EntryPoints.CommonOutputs+RegressionOutput
 Trainers.FastTreeBinaryClassifier	Uses a logit-boost boosted tree learner to perform binary classification.	Microsoft.ML.Trainers.FastTree.FastTree	TrainBinary	Microsoft.ML.Trainers.FastTree.FastTreeBinaryClassificationTrainer+Options	Microsoft.ML.EntryPoints.CommonOutputs+BinaryClassificationOutput
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
index 4f5686c3d7..8b0c46b817 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
@@ -12,8 +12,6 @@
 using Microsoft.ML.Core.Tests.UnitTests;
 using Microsoft.ML.Data;
 using Microsoft.ML.Data.IO;
-using Microsoft.ML.Ensemble;
-using Microsoft.ML.Ensemble.OutputCombiners;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.EntryPoints.JsonUtils;
 using Microsoft.ML.ImageAnalytics;
@@ -23,8 +21,8 @@
 using Microsoft.ML.LightGBM;
 using Microsoft.ML.Model.Onnx;
 using Microsoft.ML.TestFramework.Attributes;
-using Microsoft.ML.TimeSeriesProcessing;
 using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Trainers.HalLearners;
 using Microsoft.ML.Trainers.PCA;
@@ -34,6 +32,7 @@
 using Microsoft.ML.Transforms.Normalizers;
 using Microsoft.ML.Transforms.Projections;
 using Microsoft.ML.Transforms.Text;
+using Microsoft.ML.Transforms.TimeSeries;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using Xunit;
diff --git a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
index 0af5ad72b5..464fd0dc59 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
@@ -15,12 +15,12 @@ namespace Microsoft.ML.RunTests
     using Microsoft.Data.DataView;
     using Microsoft.ML;
     using Microsoft.ML.Data;
-    using Microsoft.ML.Ensemble;
     using Microsoft.ML.EntryPoints;
     using Microsoft.ML.Internal.Utilities;
     using Microsoft.ML.LightGBM;
     using Microsoft.ML.TestFramework;
     using Microsoft.ML.Trainers;
+    using Microsoft.ML.Trainers.Ensemble;
     using Microsoft.ML.Trainers.FastTree;
     using Microsoft.ML.Trainers.HalLearners;
     using Microsoft.ML.Trainers.Online;
diff --git a/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs b/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
index 36fccc7980..51f9c7e465 100644
--- a/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
+++ b/test/Microsoft.ML.TestFramework/EnvironmentExtensions.cs
@@ -3,9 +3,9 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.ML.Data;
-using Microsoft.ML.Ensemble;
 using Microsoft.ML.EntryPoints;
 using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.Ensemble;
 using Microsoft.ML.Trainers.FastTree;
 using Microsoft.ML.Trainers.KMeans;
 using Microsoft.ML.Trainers.PCA;
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
index e79da61465..bfb8a7999c 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeries.cs
@@ -5,7 +5,7 @@
 using System.IO;
 using System.Linq;
 using Microsoft.ML.TestFramework.Attributes;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 using Xunit;
 using Xunit.Abstractions;
 
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
index 9b64a68abd..8b190e0769 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
@@ -8,8 +8,7 @@
 using Microsoft.ML.Data;
 using Microsoft.ML.RunTests;
 using Microsoft.ML.TestFramework.Attributes;
-using Microsoft.ML.TimeSeries;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 using Xunit;
 
 namespace Microsoft.ML.Tests
diff --git a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesEstimatorTests.cs b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesEstimatorTests.cs
index e65c9da724..fa77255e3c 100644
--- a/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesEstimatorTests.cs
+++ b/test/Microsoft.ML.TimeSeries.Tests/TimeSeriesEstimatorTests.cs
@@ -5,7 +5,7 @@
 using System.Collections.Generic;
 using Microsoft.ML.Data;
 using Microsoft.ML.RunTests;
-using Microsoft.ML.TimeSeriesProcessing;
+using Microsoft.ML.Transforms.TimeSeries;
 using Xunit;
 using Xunit.Abstractions;
 

From f4f03ba7101f16c231eecd58272197278be2a3d8 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 11:23:53 -0800
Subject: [PATCH 14/23] Addressed PR comments

---
 .../AveragedPerceptron.cs                     | 61 -------------------
 .../AveragedPerceptron.cs                     | 45 ++++++++++++++
 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs |  9 ++-
 .../Microsoft.ML.SamplesUtils.csproj          |  2 +
 .../SamplesDatasetUtils.cs                    | 25 +++++++-
 .../Standard/Online/AveragedLinear.cs         | 34 ++++++++---
 .../Standard/Online/AveragedPerceptron.cs     | 12 ++--
 .../Standard/Online/LinearSvm.cs              |  2 +-
 .../Standard/Online/OnlineGradientDescent.cs  |  2 +-
 .../Standard/Online/OnlineLinear.cs           | 33 +++++-----
 .../StandardLearnersCatalog.cs                | 13 ++--
 .../TestPredictors.cs                         |  2 +-
 .../Scenarios/Api/TestApi.cs                  |  2 +-
 test/Microsoft.ML.Tests/Scenarios/OvaTest.cs  |  2 +-
 14 files changed, 137 insertions(+), 107 deletions(-)
 delete mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
deleted file mode 100644
index 8871711b61..0000000000
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Microsoft.ML;
-
-namespace Microsoft.ML.Samples.Dynamic.BinaryClassification
-{
-    public static class AveragedPerceptron
-    {
-        public static void Example()
-        {
-            // In this examples we will use the adult income dataset. The goal is to predict
-            // if a person's income is above $50K or not, based on different pieces of information about that person.
-            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
-
-            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
-            // as a catalog of available operations and as the source of randomness.
-            // Setting the seed to a fixed number in this examples to make outputs deterministic.
-            var mlContext = new MLContext(seed: 0);
-
-            // Download the dataset and load it as IDataView
-            var data = SamplesUtils.DatasetUtils.LoadAdultDataset(mlContext);
-
-            // Leave out 10% of data for testing
-            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
-
-            // Create data processing pipeline
-            var pipeline =
-                // Convert categorical features to one-hot vectors
-                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
-                // Combine all features into one feature vector
-                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
-                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num", 
-                    "capital-gain", "capital-loss", "hours-per-week"))
-                // Min-max normalized all the features
-                .Append(mlContext.Transforms.Normalize("Features"))
-                // Add the trainer
-                .Append(mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features"));
-
-            // Fit this pipeline to the training data
-            var model = pipeline.Fit(trainData);
-
-            // Evaluate how the model is doing on the test data
-            var dataWithPredictions = model.Transform(testData);
-            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
-            SamplesUtils.ConsoleUtils.PrintBinaryClassificationMetrics(metrics);
-
-            // Output:
-            // Accuracy: 0.85
-            // AUC: 0.90
-            // F1 Score: 0.66
-            // Negative Precision: 0.89
-            // Negative Recall: 0.91
-            // Positive Precision: 0.69
-            // Positive Recall: 0.63
-        }
-    }
-}
\ No newline at end of file
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
new file mode 100644
index 0000000000..35bdefa434
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
@@ -0,0 +1,45 @@
+using Microsoft.ML;
+
+namespace Microsoft.ML.Samples.Dynamic.Trainers.BinaryClassification
+{
+    public static class AveragedPerceptron
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this example to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download and featurize the dataset
+            var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Create data training pipeline
+            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features");
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.85
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.91
+            // Positive Precision: 0.69
+            // Positive Recall: 0.63
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
index 814a251d6a..83fafd8658 100644
--- a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
@@ -5,9 +5,16 @@
 
 namespace Microsoft.ML.SamplesUtils
 {
+    /// <summary>
+    /// Utilities for creating console outputs in samples' code.
+    /// </summary>
     public static class ConsoleUtils
     {
-        public static void PrintBinaryClassificationMetrics(BinaryClassificationMetrics metrics)
+        /// <summary>
+        /// Pretty-print BinaryClassificationMetrics objects.
+        /// </summary>
+        /// <param name="metrics">Binary classification metrics.</param>
+        public static void PrintMetrics(BinaryClassificationMetrics metrics)
         {
             Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
             Console.WriteLine($"AUC: {metrics.Auc:F2}");
diff --git a/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj b/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
index e4d6c5d504..0bdb047d42 100644
--- a/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
+++ b/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
@@ -6,7 +6,9 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\Microsoft.ML.Core\Microsoft.ML.Core.csproj" />
     <ProjectReference Include="..\Microsoft.ML.Data\Microsoft.ML.Data.csproj" />
+    <ProjectReference Include="..\Microsoft.ML.Transforms\Microsoft.ML.Transforms.csproj" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index 724f4d10f8..e6f45c0eeb 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -7,6 +7,7 @@
 using System.IO;
 using System.Net;
 using Microsoft.Data.DataView;
+using Microsoft.ML;
 using Microsoft.ML.Data;
 
 namespace Microsoft.ML.SamplesUtils
@@ -81,7 +82,7 @@ public static string DownloadSentimentDataset()
         public static string DownloadAdultDataset()
             => Download("https://raw.githubusercontent.com/dotnet/machinelearning/244a8c2ac832657af282aa312d568211698790aa/test/data/adult.train", "adult.txt");
 
-        public static IDataView LoadAdultDataset(MLContext mlContext)
+        public static IDataView LoadFeaturizedAdultDataset(MLContext mlContext)
         {
             // Download the file
             string dataFile = DownloadAdultDataset();
@@ -110,8 +111,28 @@ public static IDataView LoadAdultDataset(MLContext mlContext)
                 hasHeader: true
             );
 
-            return reader.Read(dataFile);
+            // Create data featurizing pipeline
+            var pipeline =
+                // Convert categorical features to one-hot vectors
+                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
+                // Combine all features into one feature vector
+                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
+                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num",
+                    "capital-gain", "capital-loss", "hours-per-week"))
+                // Min-max normalized all the features
+                .Append(mlContext.Transforms.Normalize("Features"));
+
+            var data = reader.Read(dataFile);
+            var featurizedData = pipeline.Fit(data).Transform(data);
+            return featurizedData;
         }
+
         /// <summary>
         /// Downloads the breast cancer dataset from the ML.NET repo.
         /// </summary>
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
index 0a73a0ac3b..6460dcc48e 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
@@ -19,7 +19,7 @@ namespace Microsoft.ML.Trainers.Online
     public abstract class AveragedLinearArguments : OnlineLinearArguments
     {
         /// <summary>
-        /// <a href="tmpurl_lr">Learning rate</a>
+        /// <a href="tmpurl_lr">Learning rate</a>.
         /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)]
         [TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")]
@@ -27,9 +27,12 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
         public float LearningRate = AveragedDefaultArgs.LearningRate;
 
         /// <summary>
-        /// <see langword="true" /> to decrease the <a href="tmpurl_lr">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
-        /// Default is <see langword="false" />.
+        /// Determine whether to decrease the <see cref="LearningRate"/> or not.
         /// </summary>
+        /// <value>
+        /// <see langword="true" /> to decrease the <see cref="LearningRate"/> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)]
         [TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")]
         [TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })]
@@ -37,16 +40,21 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
 
         /// <summary>
         /// Number of examples after which weights will be reset to the current average.
-        /// Default is <see langword="null" />, which disables this feature.
         /// </summary>
+        /// <value>
+        /// Default is <see langword="null" />, which disables this feature.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of examples after which weights will be reset to the current average", ShortName = "numreset")]
         public long? ResetWeightsAfterXExamples = null;
 
         /// <summary>
+        /// Determines when to update averaged weights.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> to update averaged weights only when loss is nonzero.
         /// <see langword="false" /> to update averaged weights on every example.
         /// Default is <see langword="true" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Instead of updating averaged weights on every example, only update when loss is nonzero", ShortName = "lazy")]
         public bool DoLazyUpdates = true;
 
@@ -60,23 +68,31 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
 
         /// <summary>
         /// Extra weight given to more recent updates.
-        /// Default is 0, i.e. no extra gain.
         /// </summary>
+        /// <value>
+        /// Default is 0, i.e. no extra gain.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Extra weight given to more recent updates", ShortName = "rg")]
         public float RecencyGain = 0;
 
         /// <summary>
+        /// Determines whether <see cref="RecencyGain"/> is multiplicative or additive.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> means <see cref="RecencyGain"/> is multiplicative.
         /// <see langword="false" /> means <see cref="RecencyGain"/> is additive.
         /// Default is <see langword="false" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether Recency Gain is multiplicative (vs. additive)", ShortName = "rgm")]
         public bool RecencyGainMulti = false;
 
         /// <summary>
+        /// Determines whether to do averaging or not.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> to do averaging; otherwise, <see langword="false" />.
         /// Default is <see langword="true" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Do averaging?", ShortName = "avg")]
         public bool Averaged = true;
 
@@ -84,7 +100,7 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
         /// The inexactness tolerance for averaging.
         /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "The inexactness tolerance for averaging", ShortName = "avgtol")]
-        public float AveragedTolerance = (float)1e-2;
+        internal float AveragedTolerance = (float)1e-2;
 
         [BestFriend]
         internal class AveragedDefaultArgs : OnlineDefaultArgs
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
index 2e82f8af5e..8de643f9d3 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
@@ -27,8 +27,10 @@ namespace Microsoft.ML.Trainers.Online
 {
     /// <summary>
     /// This is averaged perceptron trainer.
-    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
     /// </summary>
+    /// <remarks>
+    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+    /// </remarks>
     public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPredictionTransformer<LinearBinaryModelParameters>, LinearBinaryModelParameters>
     {
         public const string LoadNameValue = "AveragedPerceptron";
@@ -41,7 +43,7 @@ public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPred
         public sealed class Options : AveragedLinearArguments
         {
             /// <summary>
-            /// The custom <a href="tmpurl_loss">loss</a>. Default is hinge loss.
+            /// The custom <a href="tmpurl_loss">loss</a>.
             /// </summary>
             [Argument(ArgumentType.Multiple, HelpText = "Loss Function", ShortName = "loss", SortOrder = 50)]
             public ISupportClassificationLossFactory LossFunction = new HingeLoss.Arguments();
@@ -108,9 +110,9 @@ internal AveragedPerceptronTrainer(IHostEnvironment env, Options options)
         /// <param name="featureColumn">The name of the feature column.</param>
         /// <param name="weights">The optional name of the weights column.</param>
         /// <param name="learningRate">The learning rate. </param>
-        /// <param name="decreaseLearningRate">Wheather to decrease learning rate as iterations progress.</param>
+        /// <param name="decreaseLearningRate">Whether to decrease learning rate as iterations progress.</param>
         /// <param name="l2RegularizerWeight">L2 Regularization Weight.</param>
-        /// <param name="numIterations">The number of training iteraitons.</param>
+        /// <param name="numIterations">The number of training iterations.</param>
         internal AveragedPerceptronTrainer(IHostEnvironment env,
             string labelColumn = DefaultColumnNames.Label,
             string featureColumn = DefaultColumnNames.Features,
@@ -128,7 +130,7 @@ internal AveragedPerceptronTrainer(IHostEnvironment env,
                 LearningRate = learningRate,
                 DecreaseLearningRate = decreaseLearningRate,
                 L2RegularizerWeight = l2RegularizerWeight,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
                 LossFunction = new TrivialFactory(lossFunction ?? new HingeLoss())
             })
         {
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
index 184a6554aa..a214ce9505 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
@@ -240,7 +240,7 @@ internal LinearSvmTrainer(IHostEnvironment env,
                 LabelColumn = labelColumn,
                 FeatureColumn = featureColumn,
                 InitialWeights = weightsColumn,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
             })
         {
         }
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
index 39ed31a6ec..4c481904c3 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
@@ -115,7 +115,7 @@ internal OnlineGradientDescentTrainer(IHostEnvironment env,
                 LearningRate = learningRate,
                 DecreaseLearningRate = decreaseLearningRate,
                 L2RegularizerWeight = l2RegularizerWeight,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
                 LabelColumn = labelColumn,
                 FeatureColumn = featureColumn,
                 InitialWeights = weightsColumn,
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
index 4e1cb6e341..6ba61f77f5 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
@@ -26,7 +26,7 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter", SortOrder = 50)]
         [TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")]
         [TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)]
-        public int NumIterations = OnlineDefaultArgs.NumIterations;
+        public int NumberOfIterations = OnlineDefaultArgs.NumIterations;
 
         /// <summary>
         /// Initial weights and bias, comma-separated.
@@ -36,12 +36,16 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         public string InitialWeights;
 
         /// <summary>
-        /// Initial weights scale.
+        /// Initial weights and bias scale.
         /// </summary>
-        [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts", SortOrder = 140)]
+        /// <value>
+        /// This property is only used if the provided value is positive and <see cref="InitialWeights"/> is not specified.
+        /// The weights and bias will be randomly selected from InitialWeights * [-0.5,0.5] interval with uniform distribution.
+        /// </value>
+        [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts, initWtsDiameter", SortOrder = 140)]
         [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
         [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
-        public float InitWtsDiameter = 0;
+        public float InitialWeightsDiameter = 0;
 
         /// <summary>
         /// <see langword="true" /> to shuffle data for each training iteration; otherwise, <see langword="false" />.
@@ -51,12 +55,6 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         [TlcModule.SweepableDiscreteParamAttribute("Shuffle", new object[] { false, true })]
         public bool Shuffle = true;
 
-        /// <summary>
-        /// Size of cache when trained in Scope.
-        /// </summary>
-        [Argument(ArgumentType.AtMostOnce, HelpText = "Size of cache when trained in Scope", ShortName = "cache")]
-        public int StreamingCacheSize = 1000000;
-
         [BestFriend]
         internal class OnlineDefaultArgs
         {
@@ -151,13 +149,13 @@ protected TrainStateBase(IChannel ch, int numFeatures, LinearModelParameters pre
                     Weights = new VBuffer<float>(numFeatures, weightValues);
                     Bias = float.Parse(weightStr[numFeatures], CultureInfo.InvariantCulture);
                 }
-                else if (parent.Args.InitWtsDiameter > 0)
+                else if (parent.Args.InitialWeightsDiameter > 0)
                 {
                     var weightValues = new float[numFeatures];
                     for (int i = 0; i < numFeatures; i++)
-                        weightValues[i] = parent.Args.InitWtsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
+                        weightValues[i] = parent.Args.InitialWeightsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
                     Weights = new VBuffer<float>(numFeatures, weightValues);
-                    Bias = parent.Args.InitWtsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
+                    Bias = parent.Args.InitialWeightsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
                 }
                 else if (numFeatures <= 1000)
                     Weights = VBufferUtils.CreateDense<float>(numFeatures);
@@ -255,9 +253,8 @@ private protected OnlineLinearTrainer(OnlineLinearArguments args, IHostEnvironme
             : base(Contracts.CheckRef(env, nameof(env)).Register(name), TrainerUtils.MakeR4VecFeature(args.FeatureColumn), label, TrainerUtils.MakeR4ScalarWeightColumn(args.InitialWeights))
         {
             Contracts.CheckValue(args, nameof(args));
-            Contracts.CheckUserArg(args.NumIterations > 0, nameof(args.NumIterations), UserErrorPositive);
-            Contracts.CheckUserArg(args.InitWtsDiameter >= 0, nameof(args.InitWtsDiameter), UserErrorNonNegative);
-            Contracts.CheckUserArg(args.StreamingCacheSize > 0, nameof(args.StreamingCacheSize), UserErrorPositive);
+            Contracts.CheckUserArg(args.NumberOfIterations > 0, nameof(args.NumberOfIterations), UserErrorPositive);
+            Contracts.CheckUserArg(args.InitialWeightsDiameter >= 0, nameof(args.InitialWeightsDiameter), UserErrorNonNegative);
 
             Args = args;
             Name = name;
@@ -307,7 +304,7 @@ private void TrainCore(IChannel ch, RoleMappedData data, TrainStateBase state)
 
             var cursorFactory = new FloatLabelCursor.Factory(data, cursorOpt);
             long numBad = 0;
-            while (state.Iteration < Args.NumIterations)
+            while (state.Iteration < Args.NumberOfIterations)
             {
                 state.BeginIteration(ch);
 
@@ -325,7 +322,7 @@ private void TrainCore(IChannel ch, RoleMappedData data, TrainStateBase state)
             {
                 ch.Warning(
                     "Skipped {0} instances with missing features during training (over {1} iterations; {2} inst/iter)",
-                    numBad, Args.NumIterations, numBad / Args.NumIterations);
+                    numBad, Args.NumberOfIterations, numBad / Args.NumberOfIterations);
             }
         }
 
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 01bbc7f60a..0fe34e2b2e 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -194,11 +194,12 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// Predict a target using a linear binary classification model trained with averaged perceptron trainer.
         /// </summary>
         /// <remarks>
-        /// Perceptron is a classification algorithm that makes its predictions based on a linear function.
-        /// For instance with feature values f0, f1,..., f_D-1, the prediction is given by the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
+        /// Perceptron is a classification algorithm that makes its predictions by finding a separating hyperplane.
+        /// For instance, with feature values f0, f1,..., f_D-1, the prediction is given by determining what side of the hyperplane the point falls into.
+        /// That is the same as the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
         ///
-        /// Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
-        /// The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
+        /// The perceptron is an online algorithm, which means it processes the instances in the training set one at a time.
+        /// It starts with a set of initial weights (zero, random, or initialized from a previous learner). Then, for each example in the training set, the weighted sum of the features (sigma[0, D-1] (w_i * f_i)) is computed.
         /// If this value has the same sign as the label of the current example, the weights remain the same.If they have opposite signs,
         /// the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
         /// multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate.In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
@@ -218,7 +219,7 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// <param name="weights">The optional example weights.</param>
         /// <param name="learningRate"><a href="tmpurl_lr">Learning rate</a>.</param>
         /// <param name="decreaseLearningRate">
-        /// <see langword="true" /> to decrease the <a href="tmpurl_calib">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// <see langword="true" /> to decrease the <paramref name="learningRate"/> as iterations progress; otherwise, <see langword="false" />.
         /// Default is <see langword="false" />.
         /// </param>
         /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
@@ -252,7 +253,7 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         /// For usage details, please see <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
-        /// <param name="options">Advanced trainer options.</param>
+        /// <param name="options">Trainer options.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {
diff --git a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
index 339bede3e5..8d523ade17 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
@@ -748,7 +748,7 @@ public void TestEnsembleCombiner()
                 {
                     FeatureColumn = "Features",
                     LabelColumn = DefaultColumnNames.Label,
-                    NumIterations = 2,
+                    NumberOfIterations = 2,
                     TrainingData = dataView,
                     NormalizeFeatures = NormalizeOption.No
                 }).PredictorModel,
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
index bc43d688fb..c762401e74 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
@@ -182,7 +182,7 @@ public void TrainAveragedPerceptronWithCache()
             var cached = mlContext.Data.Cache(xf);
 
             var estimator = mlContext.BinaryClassification.Trainers.AveragedPerceptron(
-                new AveragedPerceptronTrainer.Options { NumIterations = 2 });
+                new AveragedPerceptronTrainer.Options { NumberOfIterations = 2 });
 
             estimator.Fit(cached).Transform(cached);
 
diff --git a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
index ecea5411a6..c708bb95b9 100644
--- a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
@@ -133,7 +133,7 @@ public void OvaLinearSvm()
 
             // Pipeline
             var pipeline = mlContext.MulticlassClassification.Trainers.OneVersusAll(
-                mlContext.BinaryClassification.Trainers.LinearSupportVectorMachines(new LinearSvmTrainer.Options { NumIterations = 100 }),
+                mlContext.BinaryClassification.Trainers.LinearSupportVectorMachines(new LinearSvmTrainer.Options { NumberOfIterations = 100 }),
                 useProbabilities: false);
 
             var model = pipeline.Fit(data);

From ceb3aa299b6971b6c40e337facf3a286e0210f04 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 11:47:15 -0800
Subject: [PATCH 15/23] Added sample for the second overload with trainer
 options.

---
 .../AveragedPerceptronWithOptions.cs          | 58 +++++++++++++++++++
 .../StandardLearnersCatalog.cs                |  7 +++
 2 files changed, 65 insertions(+)
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
new file mode 100644
index 0000000000..eaf8066398
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
@@ -0,0 +1,58 @@
+using Microsoft.ML;
+using Microsoft.ML.Trainers.Online;
+
+namespace Microsoft.ML.Samples.Dynamic.Trainers.BinaryClassification
+{
+    public static class AveragedPerceptronWithOptions
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this example to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download and featurize the dataset
+            var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Define the trainer options
+            var options = new AveragedPerceptronTrainer.Options()
+            {
+                LossFunction = new SmoothedHingeLoss.Arguments(),
+                LearningRate = 0.1f,
+                DoLazyUpdates = false,
+                RecencyGain = 0.1f,
+                NumberOfIterations = 10,
+                LabelColumn = "IsOver50K",
+                FeatureColumn = "Features"
+            };
+
+            // Create data training pipeline
+            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron(options);
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.86
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.93
+            // Positive Precision: 0.72
+            // Positive Recall: 0.61
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 0fe34e2b2e..d190a3509c 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -254,6 +254,13 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="options">Trainer options.</param>
+        /// <example>
+        /// <format type="text/markdown">
+        /// <![CDATA[
+        /// [!code-csharp[AveragedPerceptron](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptronWithOptions.cs)]
+        /// ]]>
+        /// </format>
+        /// </example>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {

From 56607bae5076f74db7b213dd958fee3154a99333 Mon Sep 17 00:00:00 2001
From: Ivan Matantsev <Ivanidze.kirov@gmail.com>
Date: Tue, 12 Feb 2019 13:53:57 -0800
Subject: [PATCH 16/23] Get rid of value tuples in TrainTest and
 CrossValidation (#2507)

---
 docs/code/MlNetCookBook.md                    |   8 +-
 .../Dynamic/Calibrator.cs                     |   6 +-
 .../Dynamic/LogisticRegression.cs             |   6 +-
 .../Microsoft.ML.Samples/Dynamic/SDCA.cs      |   4 +-
 src/Microsoft.ML.Data/TrainCatalog.cs         | 124 +++++++++++++++---
 .../RecommenderCatalog.cs                     |   4 +-
 .../TrainingStaticExtensions.cs               |  28 ++--
 .../Prediction.cs                             |   6 +-
 .../Validation.cs                             |   8 +-
 .../CookbookSamplesDynamicApi.cs              |   8 +-
 .../Scenarios/Api/TestApi.cs                  |  20 +--
 11 files changed, 154 insertions(+), 68 deletions(-)

diff --git a/docs/code/MlNetCookBook.md b/docs/code/MlNetCookBook.md
index 1551e275d3..bb5e866c04 100644
--- a/docs/code/MlNetCookBook.md
+++ b/docs/code/MlNetCookBook.md
@@ -825,12 +825,12 @@ var pipeline =
     .Append(mlContext.MulticlassClassification.Trainers.StochasticDualCoordinateAscent());
 
 // Split the data 90:10 into train and test sets, train and evaluate.
-var (trainData, testData) = mlContext.MulticlassClassification.TrainTestSplit(data, testFraction: 0.1);
+var split = mlContext.MulticlassClassification.TrainTestSplit(data, testFraction: 0.1);
 
 // Train the model.
-var model = pipeline.Fit(trainData);
+var model = pipeline.Fit(split.TrainSet);
 // Compute quality metrics on the test set.
-var metrics = mlContext.MulticlassClassification.Evaluate(model.Transform(testData));
+var metrics = mlContext.MulticlassClassification.Evaluate(model.Transform(split.TestSet));
 Console.WriteLine(metrics.AccuracyMicro);
 
 // Now run the 5-fold cross-validation experiment, using the same pipeline.
@@ -838,7 +838,7 @@ var cvResults = mlContext.MulticlassClassification.CrossValidate(data, pipeline,
 
 // The results object is an array of 5 elements. For each of the 5 folds, we have metrics, model and scored test data.
 // Let's compute the average micro-accuracy.
-var microAccuracies = cvResults.Select(r => r.metrics.AccuracyMicro);
+var microAccuracies = cvResults.Select(r => r.Metrics.AccuracyMicro);
 Console.WriteLine(microAccuracies.Average());
 
 ```
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Calibrator.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Calibrator.cs
index eb46a9683a..ed75040e77 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Calibrator.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Calibrator.cs
@@ -43,7 +43,7 @@ public static void Calibration()
             var data = reader.Read(dataFile);
 
             // Split the dataset into two parts: one used for training, the other to train the calibrator
-            var (trainData, calibratorTrainingData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+            var split = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
 
             // Featurize the text column through the FeaturizeText API. 
             // Then append the StochasticDualCoordinateAscentBinary binary classifier, setting the "Label" column as the label of the dataset, and 
@@ -56,12 +56,12 @@ public static void Calibration()
                         loss: new HingeLoss())); // By specifying loss: new HingeLoss(), StochasticDualCoordinateAscent will train a support vector machine (SVM).
 
             // Fit the pipeline, and get a transformer that knows how to score new data.  
-            var transformer = pipeline.Fit(trainData);
+            var transformer = pipeline.Fit(split.TrainSet);
             IPredictor model = transformer.LastTransformer.Model;
 
             // Let's score the new data. The score will give us a numerical estimation of the chance that the particular sample 
             // bears positive sentiment. This estimate is relative to the numbers obtained. 
-            var scoredData = transformer.Transform(calibratorTrainingData);
+            var scoredData = transformer.Transform(split.TestSet);
             var scoredDataPreview = scoredData.Preview();
 
             PrintRowViewValues(scoredDataPreview);
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/LogisticRegression.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/LogisticRegression.cs
index 4b25bdc805..f85b68bb7e 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/LogisticRegression.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/LogisticRegression.cs
@@ -57,7 +57,7 @@ public static void LogisticRegression()
 
             IDataView data = reader.Read(dataFilePath);
 
-            var (trainData, testData) = ml.BinaryClassification.TrainTestSplit(data, testFraction: 0.2);
+            var split = ml.BinaryClassification.TrainTestSplit(data, testFraction: 0.2);
 
             var pipeline = ml.Transforms.Concatenate("Text", "workclass", "education", "marital-status",
                     "relationship", "ethnicity", "sex", "native-country")
@@ -66,9 +66,9 @@ public static void LogisticRegression()
                     "education-num", "capital-gain", "capital-loss", "hours-per-week"))
                 .Append(ml.BinaryClassification.Trainers.LogisticRegression());
 
-            var model = pipeline.Fit(trainData);
+            var model = pipeline.Fit(split.TrainSet);
 
-            var dataWithPredictions = model.Transform(testData);
+            var dataWithPredictions = model.Transform(split.TestSet);
 
             var metrics = ml.BinaryClassification.Evaluate(dataWithPredictions);
 
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/SDCA.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/SDCA.cs
index 341827bde3..0f06b9c905 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/SDCA.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/SDCA.cs
@@ -54,7 +54,7 @@ public static void SDCA_BinaryClassification()
             // Step 3: Run Cross-Validation on this pipeline.
             var cvResults = mlContext.BinaryClassification.CrossValidate(data, pipeline, labelColumn: "Sentiment");
 
-            var accuracies = cvResults.Select(r => r.metrics.Accuracy);
+            var accuracies = cvResults.Select(r => r.Metrics.Accuracy);
             Console.WriteLine(accuracies.Average());
 
             // If we wanted to specify more advanced parameters for the algorithm, 
@@ -70,7 +70,7 @@ public static void SDCA_BinaryClassification()
 
             // Run Cross-Validation on this second pipeline.
             var cvResults_advancedPipeline = mlContext.BinaryClassification.CrossValidate(data, pipeline, labelColumn: "Sentiment", numFolds: 3);
-            accuracies = cvResults_advancedPipeline.Select(r => r.metrics.Accuracy);
+            accuracies = cvResults_advancedPipeline.Select(r => r.Metrics.Accuracy);
             Console.WriteLine(accuracies.Average());
 
         }
diff --git a/src/Microsoft.ML.Data/TrainCatalog.cs b/src/Microsoft.ML.Data/TrainCatalog.cs
index d8b4c53265..586ac66bd0 100644
--- a/src/Microsoft.ML.Data/TrainCatalog.cs
+++ b/src/Microsoft.ML.Data/TrainCatalog.cs
@@ -25,6 +25,31 @@ public abstract class TrainCatalogBase
         [BestFriend]
         internal IHostEnvironment Environment => Host;
 
+        /// <summary>
+        /// A pair of datasets, for the train and test set.
+        /// </summary>
+        public struct TrainTestData
+        {
+            /// <summary>
+            /// Training set.
+            /// </summary>
+            public readonly IDataView TrainSet;
+            /// <summary>
+            /// Testing set.
+            /// </summary>
+            public readonly IDataView TestSet;
+            /// <summary>
+            /// Create pair of datasets.
+            /// </summary>
+            /// <param name="trainSet">Training set.</param>
+            /// <param name="testSet">Testing set.</param>
+            internal TrainTestData(IDataView trainSet, IDataView testSet)
+            {
+                TrainSet = trainSet;
+                TestSet = testSet;
+            }
+        }
+
         /// <summary>
         /// Split the dataset into the train set and test set according to the given fraction.
         /// Respects the <paramref name="stratificationColumn"/> if provided.
@@ -37,8 +62,7 @@ public abstract class TrainCatalogBase
         /// <param name="seed">Optional parameter used in combination with the <paramref name="stratificationColumn"/>.
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
-        /// <returns>A pair of datasets, for the train and test set.</returns>
-        public (IDataView trainSet, IDataView testSet) TrainTestSplit(IDataView data, double testFraction = 0.1, string stratificationColumn = null, uint? seed = null)
+        public TrainTestData TrainTestSplit(IDataView data, double testFraction = 0.1, string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckValue(data, nameof(data));
             Host.CheckParam(0 < testFraction && testFraction < 1, nameof(testFraction), "Must be between 0 and 1 exclusive");
@@ -61,14 +85,71 @@ public abstract class TrainCatalogBase
                 Complement = false
             }, data);
 
-            return (trainFilter, testFilter);
+            return new TrainTestData(trainFilter, testFilter);
+        }
+
+        /// <summary>
+        /// Results for specific cross-validation fold.
+        /// </summary>
+        protected internal struct CrossValidationResult
+        {
+            /// <summary>
+            /// Model trained during cross validation fold.
+            /// </summary>
+            public readonly ITransformer Model;
+            /// <summary>
+            /// Scored test set with <see cref="Model"/> for this fold.
+            /// </summary>
+            public readonly IDataView Scores;
+            /// <summary>
+            /// Fold number.
+            /// </summary>
+            public readonly int Fold;
+
+            public CrossValidationResult(ITransformer model, IDataView scores, int fold)
+            {
+                Model = model;
+                Scores = scores;
+                Fold = fold;
+            }
+        }
+        /// <summary>
+        /// Results of running cross-validation.
+        /// </summary>
+        /// <typeparam name="T">Type of metric class.</typeparam>
+        public sealed class CrossValidationResult<T> where T : class
+        {
+            /// <summary>
+            /// Metrics for this cross-validation fold.
+            /// </summary>
+            public readonly T Metrics;
+            /// <summary>
+            /// Model trained during cross-validation fold.
+            /// </summary>
+            public readonly ITransformer Model;
+            /// <summary>
+            /// The scored hold-out set for this fold.
+            /// </summary>
+            public readonly IDataView ScoredHoldOutSet;
+            /// <summary>
+            /// Fold number.
+            /// </summary>
+            public readonly int Fold;
+
+            internal CrossValidationResult(ITransformer model, T metrics, IDataView scores, int fold)
+            {
+                Model = model;
+                Metrics = metrics;
+                ScoredHoldOutSet = scores;
+                Fold = fold;
+            }
         }
 
         /// <summary>
         /// Train the <paramref name="estimator"/> on <paramref name="numFolds"/> folds of the data sequentially.
         /// Return each model and each scored test dataset.
         /// </summary>
-        protected internal (IDataView scoredTestSet, ITransformer model)[] CrossValidateTrain(IDataView data, IEstimator<ITransformer> estimator,
+        protected internal CrossValidationResult[] CrossValidateTrain(IDataView data, IEstimator<ITransformer> estimator,
             int numFolds, string stratificationColumn, uint? seed = null)
         {
             Host.CheckValue(data, nameof(data));
@@ -78,7 +159,7 @@ protected internal (IDataView scoredTestSet, ITransformer model)[] CrossValidate
 
             EnsureStratificationColumn(ref data, ref stratificationColumn, seed);
 
-            Func<int, (IDataView scores, ITransformer model)> foldFunction =
+            Func<int, CrossValidationResult> foldFunction =
                 fold =>
                 {
                     var trainFilter = new RangeFilter(Host, new RangeFilter.Options
@@ -98,17 +179,17 @@ protected internal (IDataView scoredTestSet, ITransformer model)[] CrossValidate
 
                     var model = estimator.Fit(trainFilter);
                     var scoredTest = model.Transform(testFilter);
-                    return (scoredTest, model);
+                    return new CrossValidationResult(model, scoredTest, fold);
                 };
 
             // Sequential per-fold training.
             // REVIEW: we could have a parallel implementation here. We would need to
             // spawn off a separate host per fold in that case.
-            var result = new List<(IDataView scores, ITransformer model)>();
+            var result = new CrossValidationResult[numFolds];
             for (int fold = 0; fold < numFolds; fold++)
-                result.Add(foldFunction(fold));
+                result[fold] = foldFunction(fold);
 
-            return result.ToArray();
+            return result;
         }
 
         protected internal TrainCatalogBase(IHostEnvironment env, string registrationName)
@@ -263,13 +344,14 @@ public BinaryClassificationMetrics EvaluateNonCalibrated(IDataView data, string
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (BinaryClassificationMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidateNonCalibrated(
+        public CrossValidationResult<BinaryClassificationMetrics>[] CrossValidateNonCalibrated(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = DefaultColumnNames.Label,
             string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckNonEmpty(labelColumn, nameof(labelColumn));
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (EvaluateNonCalibrated(x.scoredTestSet, labelColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<BinaryClassificationMetrics>(x.Model,
+                EvaluateNonCalibrated(x.Scores, labelColumn), x.Scores, x.Fold)).ToArray();
         }
 
         /// <summary>
@@ -287,13 +369,14 @@ public BinaryClassificationMetrics EvaluateNonCalibrated(IDataView data, string
         /// train to the test set.</remarks>
         /// <param name="seed">If <paramref name="stratificationColumn"/> not present in dataset we will generate random filled column based on provided <paramref name="seed"/>.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (CalibratedBinaryClassificationMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidate(
+        public CrossValidationResult<CalibratedBinaryClassificationMetrics>[] CrossValidate(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = DefaultColumnNames.Label,
             string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckNonEmpty(labelColumn, nameof(labelColumn));
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (Evaluate(x.scoredTestSet, labelColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<CalibratedBinaryClassificationMetrics>(x.Model,
+                Evaluate(x.Scores, labelColumn), x.Scores, x.Fold)).ToArray();
         }
     }
 
@@ -369,12 +452,13 @@ public ClusteringMetrics Evaluate(IDataView data,
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (ClusteringMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidate(
+        public CrossValidationResult<ClusteringMetrics>[] CrossValidate(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = null, string featuresColumn = null,
             string stratificationColumn = null, uint? seed = null)
         {
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (Evaluate(x.scoredTestSet, label: labelColumn, features: featuresColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<ClusteringMetrics>(x.Model,
+                Evaluate(x.Scores, label: labelColumn, features: featuresColumn), x.Scores, x.Fold)).ToArray();
         }
     }
 
@@ -444,13 +528,14 @@ public MultiClassClassifierMetrics Evaluate(IDataView data, string label = Defau
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (MultiClassClassifierMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidate(
+        public CrossValidationResult<MultiClassClassifierMetrics>[] CrossValidate(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = DefaultColumnNames.Label,
             string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckNonEmpty(labelColumn, nameof(labelColumn));
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (Evaluate(x.scoredTestSet, labelColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<MultiClassClassifierMetrics>(x.Model,
+                Evaluate(x.Scores, labelColumn), x.Scores, x.Fold)).ToArray();
         }
     }
 
@@ -511,13 +596,14 @@ public RegressionMetrics Evaluate(IDataView data, string label = DefaultColumnNa
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (RegressionMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidate(
+        public CrossValidationResult<RegressionMetrics>[] CrossValidate(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = DefaultColumnNames.Label,
             string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckNonEmpty(labelColumn, nameof(labelColumn));
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (Evaluate(x.scoredTestSet, labelColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<RegressionMetrics>(x.Model,
+                Evaluate(x.Scores, labelColumn), x.Scores, x.Fold)).ToArray();
         }
     }
 
diff --git a/src/Microsoft.ML.Recommender/RecommenderCatalog.cs b/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
index 4c98db5a74..637e4962ea 100644
--- a/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
+++ b/src/Microsoft.ML.Recommender/RecommenderCatalog.cs
@@ -128,13 +128,13 @@ public RegressionMetrics Evaluate(IDataView data, string label = DefaultColumnNa
         /// If the <paramref name="stratificationColumn"/> is not provided, the random numbers generated to create it, will use this seed as value.
         /// And if it is not provided, the default value will be used.</param>
         /// <returns>Per-fold results: metrics, models, scored datasets.</returns>
-        public (RegressionMetrics metrics, ITransformer model, IDataView scoredTestData)[] CrossValidate(
+        public CrossValidationResult<RegressionMetrics>[] CrossValidate(
             IDataView data, IEstimator<ITransformer> estimator, int numFolds = 5, string labelColumn = DefaultColumnNames.Label,
             string stratificationColumn = null, uint? seed = null)
         {
             Host.CheckNonEmpty(labelColumn, nameof(labelColumn));
             var result = CrossValidateTrain(data, estimator, numFolds, stratificationColumn, seed);
-            return result.Select(x => (Evaluate(x.scoredTestSet, labelColumn), x.model, x.scoredTestSet)).ToArray();
+            return result.Select(x => new CrossValidationResult<RegressionMetrics>(x.Model, Evaluate(x.Scores, labelColumn), x.Scores, x.Fold)).ToArray();
         }
     }
 }
diff --git a/src/Microsoft.ML.StaticPipe/TrainingStaticExtensions.cs b/src/Microsoft.ML.StaticPipe/TrainingStaticExtensions.cs
index 357246a658..53992cf4ff 100644
--- a/src/Microsoft.ML.StaticPipe/TrainingStaticExtensions.cs
+++ b/src/Microsoft.ML.StaticPipe/TrainingStaticExtensions.cs
@@ -49,8 +49,8 @@ public static (DataView<T> trainSet, DataView<T> testSet) TrainTestSplit<T>(this
                 stratName = indexer.Get(column);
             }
 
-            var (trainData, testData) = catalog.TrainTestSplit(data.AsDynamic, testFraction, stratName, seed);
-            return (new DataView<T>(env, trainData, data.Shape), new DataView<T>(env, testData, data.Shape));
+            var split = catalog.TrainTestSplit(data.AsDynamic, testFraction, stratName, seed);
+            return (new DataView<T>(env, split.TrainSet, data.Shape), new DataView<T>(env, split.TestSet, data.Shape));
         }
 
         /// <summary>
@@ -105,9 +105,9 @@ public static (RegressionMetrics metrics, Transformer<TInShape, TOutShape, TTran
             var results = catalog.CrossValidate(data.AsDynamic, estimator.AsDynamic, numFolds, labelName, stratName, seed);
 
             return results.Select(x => (
-                    x.metrics,
-                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.model, data.Shape, estimator.Shape),
-                    new DataView<TOutShape>(env, x.scoredTestData, estimator.Shape)))
+                    x.Metrics,
+                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.Model, data.Shape, estimator.Shape),
+                    new DataView<TOutShape>(env, x.ScoredHoldOutSet, estimator.Shape)))
                 .ToArray();
         }
 
@@ -163,9 +163,9 @@ public static (MultiClassClassifierMetrics metrics, Transformer<TInShape, TOutSh
             var results = catalog.CrossValidate(data.AsDynamic, estimator.AsDynamic, numFolds, labelName, stratName, seed);
 
             return results.Select(x => (
-                    x.metrics,
-                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.model, data.Shape, estimator.Shape),
-                    new DataView<TOutShape>(env, x.scoredTestData, estimator.Shape)))
+                    x.Metrics,
+                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.Model, data.Shape, estimator.Shape),
+                    new DataView<TOutShape>(env, x.ScoredHoldOutSet, estimator.Shape)))
                 .ToArray();
         }
 
@@ -221,9 +221,9 @@ public static (BinaryClassificationMetrics metrics, Transformer<TInShape, TOutSh
             var results = catalog.CrossValidateNonCalibrated(data.AsDynamic, estimator.AsDynamic, numFolds, labelName, stratName, seed);
 
             return results.Select(x => (
-                    x.metrics,
-                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.model, data.Shape, estimator.Shape),
-                    new DataView<TOutShape>(env, x.scoredTestData, estimator.Shape)))
+                    x.Metrics,
+                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.Model, data.Shape, estimator.Shape),
+                    new DataView<TOutShape>(env, x.ScoredHoldOutSet, estimator.Shape)))
                 .ToArray();
         }
 
@@ -279,9 +279,9 @@ public static (CalibratedBinaryClassificationMetrics metrics, Transformer<TInSha
             var results = catalog.CrossValidate(data.AsDynamic, estimator.AsDynamic, numFolds, labelName, stratName, seed);
 
             return results.Select(x => (
-                    x.metrics,
-                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.model, data.Shape, estimator.Shape),
-                    new DataView<TOutShape>(env, x.scoredTestData, estimator.Shape)))
+                    x.Metrics,
+                    new Transformer<TInShape, TOutShape, TTransformer>(env, (TTransformer)x.Model, data.Shape, estimator.Shape),
+                    new DataView<TOutShape>(env, x.ScoredHoldOutSet, estimator.Shape)))
                 .ToArray();
         }
     }
diff --git a/test/Microsoft.ML.Functional.Tests/Prediction.cs b/test/Microsoft.ML.Functional.Tests/Prediction.cs
index 7e0ff2eb44..24cc049e8f 100644
--- a/test/Microsoft.ML.Functional.Tests/Prediction.cs
+++ b/test/Microsoft.ML.Functional.Tests/Prediction.cs
@@ -24,7 +24,7 @@ public void ReconfigurablePrediction()
             // Get the dataset, create a train and test
             var data = mlContext.Data.CreateTextLoader(TestDatasets.housing.GetLoaderColumns(), hasHeader: true)
                 .Read(BaseTestClass.GetDataPath(TestDatasets.housing.trainFilename));
-            (var train, var test) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.2);
+            var split = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.2);
 
             // Create a pipeline to train on the housing data
             var pipeline = mlContext.Transforms.Concatenate("Features", new string[] {
@@ -33,9 +33,9 @@ public void ReconfigurablePrediction()
                 .Append(mlContext.Transforms.CopyColumns("Label", "MedianHomeValue"))
                 .Append(mlContext.Regression.Trainers.OrdinaryLeastSquares());
 
-            var model = pipeline.Fit(train);
+            var model = pipeline.Fit(split.TrainSet);
 
-            var scoredTest = model.Transform(test);
+            var scoredTest = model.Transform(split.TestSet);
             var metrics = mlContext.Regression.Evaluate(scoredTest);
 
             Common.CheckMetrics(metrics);
diff --git a/test/Microsoft.ML.Functional.Tests/Validation.cs b/test/Microsoft.ML.Functional.Tests/Validation.cs
index b9bb617285..b04eff387a 100644
--- a/test/Microsoft.ML.Functional.Tests/Validation.cs
+++ b/test/Microsoft.ML.Functional.Tests/Validation.cs
@@ -40,14 +40,14 @@ void CrossValidation()
             var cvResult = mlContext.Regression.CrossValidate(data, pipeline, numFolds: 5);
 
             // Check that the results are valid
-            Assert.IsType<RegressionMetrics>(cvResult[0].metrics);
-            Assert.IsType<TransformerChain<RegressionPredictionTransformer<OlsLinearRegressionModelParameters>>>(cvResult[0].model);
-            Assert.True(cvResult[0].scoredTestData is IDataView);
+            Assert.IsType<RegressionMetrics>(cvResult[0].Metrics);
+            Assert.IsType<TransformerChain<RegressionPredictionTransformer<OlsLinearRegressionModelParameters>>>(cvResult[0].Model);
+            Assert.True(cvResult[0].ScoredHoldOutSet is IDataView);
             Assert.Equal(5, cvResult.Length);
 
             // And validate the metrics
             foreach (var result in cvResult)
-                Common.CheckMetrics(result.metrics);
+                Common.CheckMetrics(result.Metrics);
         }
     }
 }
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
index a0ef508417..dd05dfd549 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamplesDynamicApi.cs
@@ -428,12 +428,12 @@ private void CrossValidationOn(string dataPath)
                 .Append(mlContext.MulticlassClassification.Trainers.StochasticDualCoordinateAscent());
 
             // Split the data 90:10 into train and test sets, train and evaluate.
-            var (trainData, testData) = mlContext.MulticlassClassification.TrainTestSplit(data, testFraction: 0.1);
+            var split = mlContext.MulticlassClassification.TrainTestSplit(data, testFraction: 0.1);
 
             // Train the model.
-            var model = pipeline.Fit(trainData);
+            var model = pipeline.Fit(split.TrainSet);
             // Compute quality metrics on the test set.
-            var metrics = mlContext.MulticlassClassification.Evaluate(model.Transform(testData));
+            var metrics = mlContext.MulticlassClassification.Evaluate(model.Transform(split.TestSet));
             Console.WriteLine(metrics.AccuracyMicro);
 
             // Now run the 5-fold cross-validation experiment, using the same pipeline.
@@ -441,7 +441,7 @@ private void CrossValidationOn(string dataPath)
 
             // The results object is an array of 5 elements. For each of the 5 folds, we have metrics, model and scored test data.
             // Let's compute the average micro-accuracy.
-            var microAccuracies = cvResults.Select(r => r.metrics.AccuracyMicro);
+            var microAccuracies = cvResults.Select(r => r.Metrics.AccuracyMicro);
             Console.WriteLine(microAccuracies.Average());
         }
 
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
index bc43d688fb..4b7fdbfaff 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
@@ -312,22 +312,22 @@ public void TestTrainTestSplit()
             // Let's test what train test properly works with seed.
             // In order to do that, let's split same dataset, but in one case we will use default seed value,
             // and in other case we set seed to be specific value.
-            var (simpleTrain, simpleTest) = mlContext.BinaryClassification.TrainTestSplit(input);
-            var (simpleTrainWithSeed, simpleTestWithSeed) = mlContext.BinaryClassification.TrainTestSplit(input, seed: 10);
+            var simpleSplit = mlContext.BinaryClassification.TrainTestSplit(input);
+            var splitWithSeed = mlContext.BinaryClassification.TrainTestSplit(input, seed: 10);
 
             // Since test fraction is 0.1, it's much faster to compare test subsets of split.
-            var simpleTestWorkClass = getWorkclass(simpleTest);
+            var simpleTestWorkClass = getWorkclass(simpleSplit.TestSet);
 
-            var simpleWithSeedTestWorkClass = getWorkclass(simpleTestWithSeed);
+            var simpleWithSeedTestWorkClass = getWorkclass(splitWithSeed.TestSet);
             // Validate we get different test sets.
             Assert.NotEqual(simpleTestWorkClass, simpleWithSeedTestWorkClass);
 
             // Now let's do same thing but with presence of stratificationColumn.
             // Rows with same values in this stratificationColumn should end up in same subset (train or test).
             // So let's break dataset by "Workclass" column.
-            var (stratTrain, stratTest) = mlContext.BinaryClassification.TrainTestSplit(input, stratificationColumn: "Workclass");
-            var stratTrainWorkclass = getWorkclass(stratTrain);
-            var stratTestWorkClass = getWorkclass(stratTest);
+            var stratSplit = mlContext.BinaryClassification.TrainTestSplit(input, stratificationColumn: "Workclass");
+            var stratTrainWorkclass = getWorkclass(stratSplit.TrainSet);
+            var stratTestWorkClass = getWorkclass(stratSplit.TestSet);
             // Let's get unique values for "Workclass" column from train subset.
             var uniqueTrain = stratTrainWorkclass.GroupBy(x => x.ToString()).Select(x => x.First()).ToList();
             // and from test subset.
@@ -337,9 +337,9 @@ public void TestTrainTestSplit()
 
             // Let's do same thing, but this time we will choose different seed.
             // Stratification column should still break dataset properly without same values in both subsets.
-            var (stratWithSeedTrain, stratWithSeedTest) = mlContext.BinaryClassification.TrainTestSplit(input, stratificationColumn:"Workclass", seed: 1000000);
-            var stratTrainWithSeedWorkclass = getWorkclass(stratWithSeedTrain);
-            var stratTestWithSeedWorkClass = getWorkclass(stratWithSeedTest);
+            var stratSeed = mlContext.BinaryClassification.TrainTestSplit(input, stratificationColumn:"Workclass", seed: 1000000);
+            var stratTrainWithSeedWorkclass = getWorkclass(stratSeed.TrainSet);
+            var stratTestWithSeedWorkClass = getWorkclass(stratSeed.TestSet);
             // Let's get unique values for "Workclass" column from train subset.
             var uniqueSeedTrain = stratTrainWithSeedWorkclass.GroupBy(x => x.ToString()).Select(x => x.First()).ToList();
             // and from test subset.

From 2b96f6daea01b09dc894b30f7b04f93042c13297 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 14:20:19 -0800
Subject: [PATCH 17/23] Fixed the failing tests

---
 .../Common/EntryPoints/core_manifest.json     | 57 ++++---------------
 .../UnitTests/TestEntryPoints.cs              |  5 +-
 2 files changed, 14 insertions(+), 48 deletions(-)

diff --git a/test/BaselineOutput/Common/EntryPoints/core_manifest.json b/test/BaselineOutput/Common/EntryPoints/core_manifest.json
index 5154957bf1..5d720ce6de 100644
--- a/test/BaselineOutput/Common/EntryPoints/core_manifest.json
+++ b/test/BaselineOutput/Common/EntryPoints/core_manifest.json
@@ -4306,7 +4306,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -4325,11 +4325,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -4467,18 +4468,6 @@
               true
             ]
           }
-        },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
         }
       ],
       "Outputs": [
@@ -13247,7 +13236,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -13266,11 +13255,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -13353,18 +13343,6 @@
             ]
           }
         },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
-        },
         {
           "Name": "BatchSize",
           "Type": "Int",
@@ -14272,7 +14250,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -14291,11 +14269,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -14410,18 +14389,6 @@
               true
             ]
           }
-        },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
         }
       ],
       "Outputs": [
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
index 8fcb00842e..7035791227 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
@@ -5448,11 +5448,10 @@ public void TestOvaMacroWithUncalibratedLearner()
                                         'RecencyGainMulti': false,
                                         'Averaged': true,
                                         'AveragedTolerance': 0.01,
-                                        'NumIterations': 1,
+                                        'NumberOfIterations': 1,
                                         'InitialWeights': null,
-                                        'InitWtsDiameter': 0.0,
+                                        'InitialWeightsDiameter': 0.0,
                                         'Shuffle': false,
-                                        'StreamingCacheSize': 1000000,
                                         'LabelColumn': 'Label',
                                         'TrainingData': '$Var_9ccc8bce4f6540eb8a244ab40585602a',
                                         'FeatureColumn': 'Features',

From 87be8dc40107662221f7852c181a26c8d58e3a9d Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Thu, 7 Feb 2019 16:00:25 -0800
Subject: [PATCH 18/23] Updated docs for AveragedPerceptron

---
 .../Standard/Online/AveragedLinear.cs         | 35 +++++++++++++++++
 .../Standard/Online/AveragedPerceptron.cs     | 19 ++++++---
 .../Standard/Online/OnlineLinear.cs           | 16 ++++++++
 .../Standard/Online/doc.xml                   | 39 -------------------
 .../StandardLearnersCatalog.cs                | 36 +++++++++++++----
 5 files changed, 93 insertions(+), 52 deletions(-)

diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
index 85da68d1f9..a0f156fb74 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
@@ -17,36 +17,71 @@ namespace Microsoft.ML.Trainers.Online
 {
     public abstract class AveragedLinearArguments : OnlineLinearArguments
     {
+        /// <summary>
+        /// <a href="tmpurl_lr">Learning rate</a>
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)]
         [TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")]
         [TlcModule.SweepableDiscreteParam("LearningRate", new object[] { 0.01, 0.1, 0.5, 1.0 })]
         public float LearningRate = AveragedDefaultArgs.LearningRate;
 
+        /// <summary>
+        /// <see langword="true" /> to decrease the <a href="tmpurl_lr">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)]
         [TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")]
         [TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })]
         public bool DecreaseLearningRate = AveragedDefaultArgs.DecreaseLearningRate;
 
+        /// <summary>
+        /// Number of examples after which weights will be reset to the current average.
+        /// Default is <see langword="null" />, which disables this feature.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of examples after which weights will be reset to the current average", ShortName = "numreset")]
         public long? ResetWeightsAfterXExamples = null;
 
+        /// <summary>
+        /// <see langword="true" /> to update averaged weights only when loss is nonzero.
+        /// <see langword="false" /> to update averaged weights on every example.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Instead of updating averaged weights on every example, only update when loss is nonzero", ShortName = "lazy")]
         public bool DoLazyUpdates = true;
 
+        /// <summary>
+        /// L2 weight for <a href='tmpurl_regularization'>regularization</a>.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization Weight", ShortName = "reg", SortOrder = 50)]
         [TGUI(Label = "L2 Regularization Weight")]
         [TlcModule.SweepableFloatParam("L2RegularizerWeight", 0.0f, 0.4f)]
         public float L2RegularizerWeight = AveragedDefaultArgs.L2RegularizerWeight;
 
+        /// <summary>
+        /// Extra weight given to more recent updates.
+        /// Default is 0, i.e. no extra gain.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Extra weight given to more recent updates", ShortName = "rg")]
         public float RecencyGain = 0;
 
+        /// <summary>
+        /// <see langword="true" /> means <see cref="RecencyGain"/> is multiplicative.
+        /// <see langword="false" /> means <see cref="RecencyGain"/> is additive.
+        /// Default is <see langword="false" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether Recency Gain is multiplicative (vs. additive)", ShortName = "rgm")]
         public bool RecencyGainMulti = false;
 
+        /// <summary>
+        /// <see langword="true" /> to do averaging; otherwise, <see langword="false" />.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Do averaging?", ShortName = "avg")]
         public bool Averaged = true;
 
+        /// <summary>
+        /// The inexactness tolerance for averaging.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "The inexactness tolerance for averaging", ShortName = "avgtol")]
         public float AveragedTolerance = (float)1e-2;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
index c3610c7dd1..ac72e2aceb 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
@@ -24,12 +24,10 @@
 
 namespace Microsoft.ML.Trainers.Online
 {
-    // This is an averaged perceptron classifier.
-    // Configurable subcomponents:
-    //     - Loss function. By default, hinge loss (aka max-margin avgd perceptron)
-    //     - Feature normalization. By default, rescaling between min and max values for every feature
-    //     - Prediction calibration to produce probabilities. Off by default, if on, uses exponential (aka Platt) calibration.
-    /// <include file='doc.xml' path='doc/members/member[@name="AP"]/*' />
+    /// <summary>
+    /// This is averaged perceptron trainer.
+    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+    /// </summary>
     public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPredictionTransformer<LinearBinaryModelParameters>, LinearBinaryModelParameters>
     {
         public const string LoadNameValue = "AveragedPerceptron";
@@ -41,12 +39,21 @@ public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPred
 
         public sealed class Options : AveragedLinearArguments
         {
+            /// <summary>
+            /// The custom <a href="tmpurl_loss">loss</a>. Default is hinge loss.
+            /// </summary>
             [Argument(ArgumentType.Multiple, HelpText = "Loss Function", ShortName = "loss", SortOrder = 50)]
             public ISupportClassificationLossFactory LossFunction = new HingeLoss.Arguments();
 
+            /// <summary>
+            /// The <a href="tmpurl_calib">calibrator</a> for producing probabilities. Default is exponential (aka Platt) calibration.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
             public ICalibratorTrainerFactory Calibrator = new PlattCalibratorTrainerFactory();
 
+            /// <summary>
+            /// The maximum number of examples to use when training the calibrator.
+            /// </summary>
             [Argument(ArgumentType.AtMostOnce, HelpText = "The maximum number of examples to use when training the calibrator", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
             public int MaxCalibrationExamples = 1000000;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
index b41890a5fa..a1ff85ae01 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
@@ -19,24 +19,40 @@ namespace Microsoft.ML.Trainers.Online
 
     public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
     {
+        /// <summary>
+        /// Number of training iterations through the data.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter", SortOrder = 50)]
         [TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")]
         [TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)]
         public int NumIterations = OnlineDefaultArgs.NumIterations;
 
+        /// <summary>
+        /// Initial weights and bias, comma-separated.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Initial Weights and bias, comma-separated", ShortName = "initweights")]
         [TGUI(NoSweep = true)]
         public string InitialWeights;
 
+        /// <summary>
+        /// Initial weights scale.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts", SortOrder = 140)]
         [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
         [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
         public float InitWtsDiameter = 0;
 
+        /// <summary>
+        /// <see langword="true" /> to shuffle data for each training iteration; otherwise, <see langword="false" />.
+        /// Default is <see langword="true" />.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether to shuffle for each training iteration", ShortName = "shuf")]
         [TlcModule.SweepableDiscreteParamAttribute("Shuffle", new object[] { false, true })]
         public bool Shuffle = true;
 
+        /// <summary>
+        /// Size of cache when trained in Scope.
+        /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Size of cache when trained in Scope", ShortName = "cache")]
         public int StreamingCacheSize = 1000000;
 
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml b/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
index 8e8f5dc2ba..292aeface5 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/doc.xml
@@ -25,44 +25,5 @@
         </code>
       </example>
     </example>
-
-    <member name="AP">
-      <summary>
-        Averaged Perceptron Binary Classifier. 
-      </summary>
-      <remarks>
-        Perceptron is a classification algorithm that makes its predictions based on a linear function.
-        I.e., for an instance with feature values f0, f1,..., f_D-1, , the prediction is given by the sign of sigma[0,D-1] ( w_i * f_i), where w_0, w_1,...,w_D-1 are the weights computed by the algorithm.
-        <para>
-          Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
-          The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
-          If this value has the same sign as the label of the current example, the weights remain the same. If they have opposite signs,
-          the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
-          multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate. In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
-          and by the gradient of some loss function (in the specific case described above, the loss is hinge-loss, whose gradient is 1 when it is non-zero).
-        </para>
-        <para>
-          In Averaged Perceptron (AKA voted-perceptron), the weight vectors are stored,
-          together with a weight that counts the number of iterations it survived (this is equivalent to storing the weight vector after every iteration, regardless of whether it was updated or not).
-          The prediction is then calculated by taking the weighted average of all the sums sigma[0, D-1] (w_i * f_i) or the different weight vectors.
-        </para>
-        <para> For more information see:</para>
-        <para><a href='https://en.wikipedia.org/wiki/Perceptron'>Wikipedia entry for Perceptron</a></para>
-        <para><a href='https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.48.8200'>Large Margin Classification Using the Perceptron Algorithm</a></para>
-      </remarks>
-    </member>
-    <example>
-      <example name="AP">
-        <code language="csharp">
-          new AveragedPerceptronBinaryClassifier
-          {
-            NumIterations = 10,
-            L2RegularizerWeight = 0.01f,
-            LossFunction = new ExpLossClassificationLossFunction()
-          }
-        </code>
-      </example>
-    </example>
-
   </members>
 </doc>
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index ae03b46bc8..736d5b1165 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -190,16 +190,37 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         }
 
         /// <summary>
-        /// Predict a target using a linear binary classification model trained with the AveragedPerceptron trainer.
+        /// Predict a target using a linear binary classification model trained with averaged perceptron trainer.
         /// </summary>
+        /// <remarks>
+        /// Perceptron is a classification algorithm that makes its predictions based on a linear function.
+        /// For instance with feature values f0, f1,..., f_D-1, the prediction is given by the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
+        ///
+        /// Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
+        /// The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
+        /// If this value has the same sign as the label of the current example, the weights remain the same.If they have opposite signs,
+        /// the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
+        /// multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate.In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
+        /// and by the gradient of some loss function (in the specific case described above, the loss is hinge-loss, whose gradient is 1 when it is non-zero).
+        ///
+        /// In Averaged Perceptron (AKA voted-perceptron), the weight vectors are stored,
+        /// together with a weight that counts the number of iterations it survived (this is equivalent to storing the weight vector after every iteration, regardless of whether it was updated or not).
+        /// The prediction is then calculated by taking the weighted average of all the sums sigma[0, D-1] (w_i * f_i) or the different weight vectors.
+        ///
+        /// For more information see <a href="https://en.wikipedia.org/wiki/Perceptron">Wikipedia entry for Perceptron</a>
+        /// or <a href="https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.48.8200">Large Margin Classification Using the Perceptron Algorithm</a>
+        /// </remarks>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="labelColumn">The name of the label column, or dependent variable.</param>
         /// <param name="featureColumn">The features, or independent variables.</param>
-        /// <param name="lossFunction">The custom loss.</param>
+        /// <param name="lossFunction">The custom <a href="tmpurl_loss">loss</a>. If <see langword="null"/>, hinge loss will be used resulting in max-margin averaged perceptron.</param>
         /// <param name="weights">The optional example weights.</param>
-        /// <param name="learningRate">The learning Rate.</param>
-        /// <param name="decreaseLearningRate">Decrease learning rate as iterations progress.</param>
-        /// <param name="l2RegularizerWeight">L2 regularization weight.</param>
+        /// <param name="learningRate"><a href="tmpurl_lr">Learning rate</a>.</param>
+        /// <param name="decreaseLearningRate">
+        /// <see langword="true" /> to decrease the <a href="tmpurl_calib">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </param>
+        /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
         /// <param name="numIterations">Number of training iterations through the data.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog,
@@ -219,10 +240,11 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         }
 
         /// <summary>
-        /// Predict a target using a linear binary classification model trained with the AveragedPerceptron trainer.
+        /// Predict a target using a linear binary classification model trained with averaged perceptron trainer using advanced options.
+        /// For trainer details, please see the remarks for <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
-        /// <param name="options">Advanced arguments to the algorithm.</param>
+        /// <param name="options">Advanced trainer options.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {

From ba0abff0bade3fb33e2c3f1e634fd0c92e2f8acf Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Fri, 8 Feb 2019 14:29:34 -0800
Subject: [PATCH 19/23] Added a sample

---
 .../AveragedPerceptron.cs                     | 61 +++++++++++++++++++
 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs | 21 +++++++
 .../SamplesDatasetUtils.cs                    | 31 ++++++++++
 .../StandardLearnersCatalog.cs                |  9 ++-
 4 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
 create mode 100644 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
new file mode 100644
index 0000000000..8871711b61
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
@@ -0,0 +1,61 @@
+using Microsoft.ML;
+
+namespace Microsoft.ML.Samples.Dynamic.BinaryClassification
+{
+    public static class AveragedPerceptron
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this examples to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download the dataset and load it as IDataView
+            var data = SamplesUtils.DatasetUtils.LoadAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Create data processing pipeline
+            var pipeline =
+                // Convert categorical features to one-hot vectors
+                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
+                // Combine all features into one feature vector
+                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
+                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num", 
+                    "capital-gain", "capital-loss", "hours-per-week"))
+                // Min-max normalized all the features
+                .Append(mlContext.Transforms.Normalize("Features"))
+                // Add the trainer
+                .Append(mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features"));
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintBinaryClassificationMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.85
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.91
+            // Positive Precision: 0.69
+            // Positive Recall: 0.63
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
new file mode 100644
index 0000000000..814a251d6a
--- /dev/null
+++ b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.ML.Data;
+
+namespace Microsoft.ML.SamplesUtils
+{
+    public static class ConsoleUtils
+    {
+        public static void PrintBinaryClassificationMetrics(BinaryClassificationMetrics metrics)
+        {
+            Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
+            Console.WriteLine($"AUC: {metrics.Auc:F2}");
+            Console.WriteLine($"F1 Score: {metrics.F1Score:F2}");
+            Console.WriteLine($"Negative Precision: {metrics.NegativePrecision:F2}");
+            Console.WriteLine($"Negative Recall: {metrics.NegativeRecall:F2}");
+            Console.WriteLine($"Positive Precision: {metrics.PositivePrecision:F2}");
+            Console.WriteLine($"Positive Recall: {metrics.PositiveRecall:F2}");
+        }
+    }
+}
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index 17ce2e3ab7..e3969ba5e3 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -86,6 +86,37 @@ public static string DownloadSentimentDataset()
         public static string DownloadAdultDataset()
             => Download("https://raw.githubusercontent.com/dotnet/machinelearning/244a8c2ac832657af282aa312d568211698790aa/test/data/adult.train", "adult.txt");
 
+        public static IDataView LoadAdultDataset(MLContext mlContext)
+        {
+            // Download the file
+            string dataFile = DownloadAdultDataset();
+
+            // Define the columns to read
+            var reader = mlContext.Data.CreateTextLoader(
+                columns: new[]
+                    {
+                        new TextLoader.Column("age", DataKind.R4, 0),
+                        new TextLoader.Column("workclass", DataKind.TX, 1),
+                        new TextLoader.Column("fnlwgt", DataKind.R4, 2),
+                        new TextLoader.Column("education", DataKind.TX, 3),
+                        new TextLoader.Column("education-num", DataKind.R4, 4),
+                        new TextLoader.Column("marital-status", DataKind.TX, 5),
+                        new TextLoader.Column("occupation", DataKind.TX, 6),
+                        new TextLoader.Column("relationship", DataKind.TX, 7),
+                        new TextLoader.Column("ethnicity", DataKind.TX, 8),
+                        new TextLoader.Column("sex", DataKind.TX, 9),
+                        new TextLoader.Column("capital-gain", DataKind.R4, 10),
+                        new TextLoader.Column("capital-loss", DataKind.R4, 11),
+                        new TextLoader.Column("hours-per-week", DataKind.R4, 12),
+                        new TextLoader.Column("native-country", DataKind.R4, 13),
+                        new TextLoader.Column("IsOver50K", DataKind.BL, 14),
+                    },
+                separatorChar: ',',
+                hasHeader: true
+            );
+
+            return reader.Read(dataFile);
+        }
         /// <summary>
         /// Downloads the breast cancer dataset from the ML.NET repo.
         /// </summary>
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 736d5b1165..f614c4098c 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -222,6 +222,13 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// </param>
         /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
         /// <param name="numIterations">Number of training iterations through the data.</param>
+        /// <example>
+        /// <format type="text/markdown">
+        /// <![CDATA[
+        /// [!code-csharp[AveragedPerceptron](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs)]
+        /// ]]>
+        /// </format>
+        /// </example>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog,
             string labelColumn = DefaultColumnNames.Label,
@@ -241,7 +248,7 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
 
         /// <summary>
         /// Predict a target using a linear binary classification model trained with averaged perceptron trainer using advanced options.
-        /// For trainer details, please see the remarks for <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+        /// For usage details, please see <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="options">Advanced trainer options.</param>

From a5538edea7faaaf2fa93c76c854c366550949c24 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 11:23:53 -0800
Subject: [PATCH 20/23] Addressed PR comments

---
 .../AveragedPerceptron.cs                     | 61 -------------------
 .../AveragedPerceptron.cs                     | 45 ++++++++++++++
 src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs |  9 ++-
 .../Microsoft.ML.SamplesUtils.csproj          |  2 +
 .../SamplesDatasetUtils.cs                    | 25 +++++++-
 .../Standard/Online/AveragedLinear.cs         | 34 ++++++++---
 .../Standard/Online/AveragedPerceptron.cs     | 12 ++--
 .../Standard/Online/LinearSvm.cs              |  2 +-
 .../Standard/Online/OnlineGradientDescent.cs  |  2 +-
 .../Standard/Online/OnlineLinear.cs           | 33 +++++-----
 .../StandardLearnersCatalog.cs                | 13 ++--
 .../TestPredictors.cs                         |  2 +-
 .../Scenarios/Api/TestApi.cs                  |  2 +-
 test/Microsoft.ML.Tests/Scenarios/OvaTest.cs  |  2 +-
 14 files changed, 137 insertions(+), 107 deletions(-)
 delete mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
deleted file mode 100644
index 8871711b61..0000000000
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptron.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Microsoft.ML;
-
-namespace Microsoft.ML.Samples.Dynamic.BinaryClassification
-{
-    public static class AveragedPerceptron
-    {
-        public static void Example()
-        {
-            // In this examples we will use the adult income dataset. The goal is to predict
-            // if a person's income is above $50K or not, based on different pieces of information about that person.
-            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
-
-            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
-            // as a catalog of available operations and as the source of randomness.
-            // Setting the seed to a fixed number in this examples to make outputs deterministic.
-            var mlContext = new MLContext(seed: 0);
-
-            // Download the dataset and load it as IDataView
-            var data = SamplesUtils.DatasetUtils.LoadAdultDataset(mlContext);
-
-            // Leave out 10% of data for testing
-            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
-
-            // Create data processing pipeline
-            var pipeline =
-                // Convert categorical features to one-hot vectors
-                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
-                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
-                // Combine all features into one feature vector
-                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
-                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num", 
-                    "capital-gain", "capital-loss", "hours-per-week"))
-                // Min-max normalized all the features
-                .Append(mlContext.Transforms.Normalize("Features"))
-                // Add the trainer
-                .Append(mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features"));
-
-            // Fit this pipeline to the training data
-            var model = pipeline.Fit(trainData);
-
-            // Evaluate how the model is doing on the test data
-            var dataWithPredictions = model.Transform(testData);
-            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
-            SamplesUtils.ConsoleUtils.PrintBinaryClassificationMetrics(metrics);
-
-            // Output:
-            // Accuracy: 0.85
-            // AUC: 0.90
-            // F1 Score: 0.66
-            // Negative Precision: 0.89
-            // Negative Recall: 0.91
-            // Positive Precision: 0.69
-            // Positive Recall: 0.63
-        }
-    }
-}
\ No newline at end of file
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
new file mode 100644
index 0000000000..35bdefa434
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
@@ -0,0 +1,45 @@
+using Microsoft.ML;
+
+namespace Microsoft.ML.Samples.Dynamic.Trainers.BinaryClassification
+{
+    public static class AveragedPerceptron
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this example to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download and featurize the dataset
+            var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Create data training pipeline
+            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features");
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.85
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.91
+            // Positive Precision: 0.69
+            // Positive Recall: 0.63
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
index 814a251d6a..83fafd8658 100644
--- a/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/ConsoleUtils.cs
@@ -5,9 +5,16 @@
 
 namespace Microsoft.ML.SamplesUtils
 {
+    /// <summary>
+    /// Utilities for creating console outputs in samples' code.
+    /// </summary>
     public static class ConsoleUtils
     {
-        public static void PrintBinaryClassificationMetrics(BinaryClassificationMetrics metrics)
+        /// <summary>
+        /// Pretty-print BinaryClassificationMetrics objects.
+        /// </summary>
+        /// <param name="metrics">Binary classification metrics.</param>
+        public static void PrintMetrics(BinaryClassificationMetrics metrics)
         {
             Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
             Console.WriteLine($"AUC: {metrics.Auc:F2}");
diff --git a/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj b/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
index e4d6c5d504..0bdb047d42 100644
--- a/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
+++ b/src/Microsoft.ML.SamplesUtils/Microsoft.ML.SamplesUtils.csproj
@@ -6,7 +6,9 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\Microsoft.ML.Core\Microsoft.ML.Core.csproj" />
     <ProjectReference Include="..\Microsoft.ML.Data\Microsoft.ML.Data.csproj" />
+    <ProjectReference Include="..\Microsoft.ML.Transforms\Microsoft.ML.Transforms.csproj" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index e3969ba5e3..918cd26a9d 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -7,6 +7,7 @@
 using System.IO;
 using System.Net;
 using Microsoft.Data.DataView;
+using Microsoft.ML;
 using Microsoft.ML.Data;
 
 namespace Microsoft.ML.SamplesUtils
@@ -86,7 +87,7 @@ public static string DownloadSentimentDataset()
         public static string DownloadAdultDataset()
             => Download("https://raw.githubusercontent.com/dotnet/machinelearning/244a8c2ac832657af282aa312d568211698790aa/test/data/adult.train", "adult.txt");
 
-        public static IDataView LoadAdultDataset(MLContext mlContext)
+        public static IDataView LoadFeaturizedAdultDataset(MLContext mlContext)
         {
             // Download the file
             string dataFile = DownloadAdultDataset();
@@ -115,8 +116,28 @@ public static IDataView LoadAdultDataset(MLContext mlContext)
                 hasHeader: true
             );
 
-            return reader.Read(dataFile);
+            // Create data featurizing pipeline
+            var pipeline =
+                // Convert categorical features to one-hot vectors
+                mlContext.Transforms.Categorical.OneHotEncoding("workclass")
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("education"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("marital-status"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("occupation"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("relationship"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("ethnicity"))
+                .Append(mlContext.Transforms.Categorical.OneHotEncoding("native-country"))
+                // Combine all features into one feature vector
+                .Append(mlContext.Transforms.Concatenate("Features", "workclass", "education", "marital-status",
+                    "occupation", "relationship", "ethnicity", "native-country", "age", "education-num",
+                    "capital-gain", "capital-loss", "hours-per-week"))
+                // Min-max normalized all the features
+                .Append(mlContext.Transforms.Normalize("Features"));
+
+            var data = reader.Read(dataFile);
+            var featurizedData = pipeline.Fit(data).Transform(data);
+            return featurizedData;
         }
+
         /// <summary>
         /// Downloads the breast cancer dataset from the ML.NET repo.
         /// </summary>
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
index a0f156fb74..9234cd2df4 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedLinear.cs
@@ -18,7 +18,7 @@ namespace Microsoft.ML.Trainers.Online
     public abstract class AveragedLinearArguments : OnlineLinearArguments
     {
         /// <summary>
-        /// <a href="tmpurl_lr">Learning rate</a>
+        /// <a href="tmpurl_lr">Learning rate</a>.
         /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)]
         [TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")]
@@ -26,9 +26,12 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
         public float LearningRate = AveragedDefaultArgs.LearningRate;
 
         /// <summary>
-        /// <see langword="true" /> to decrease the <a href="tmpurl_lr">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
-        /// Default is <see langword="false" />.
+        /// Determine whether to decrease the <see cref="LearningRate"/> or not.
         /// </summary>
+        /// <value>
+        /// <see langword="true" /> to decrease the <see cref="LearningRate"/> as iterations progress; otherwise, <see langword="false" />.
+        /// Default is <see langword="false" />.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)]
         [TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")]
         [TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })]
@@ -36,16 +39,21 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
 
         /// <summary>
         /// Number of examples after which weights will be reset to the current average.
-        /// Default is <see langword="null" />, which disables this feature.
         /// </summary>
+        /// <value>
+        /// Default is <see langword="null" />, which disables this feature.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of examples after which weights will be reset to the current average", ShortName = "numreset")]
         public long? ResetWeightsAfterXExamples = null;
 
         /// <summary>
+        /// Determines when to update averaged weights.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> to update averaged weights only when loss is nonzero.
         /// <see langword="false" /> to update averaged weights on every example.
         /// Default is <see langword="true" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Instead of updating averaged weights on every example, only update when loss is nonzero", ShortName = "lazy")]
         public bool DoLazyUpdates = true;
 
@@ -59,23 +67,31 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
 
         /// <summary>
         /// Extra weight given to more recent updates.
-        /// Default is 0, i.e. no extra gain.
         /// </summary>
+        /// <value>
+        /// Default is 0, i.e. no extra gain.
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Extra weight given to more recent updates", ShortName = "rg")]
         public float RecencyGain = 0;
 
         /// <summary>
+        /// Determines whether <see cref="RecencyGain"/> is multiplicative or additive.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> means <see cref="RecencyGain"/> is multiplicative.
         /// <see langword="false" /> means <see cref="RecencyGain"/> is additive.
         /// Default is <see langword="false" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Whether Recency Gain is multiplicative (vs. additive)", ShortName = "rgm")]
         public bool RecencyGainMulti = false;
 
         /// <summary>
+        /// Determines whether to do averaging or not.
+        /// </summary>
+        /// <value>
         /// <see langword="true" /> to do averaging; otherwise, <see langword="false" />.
         /// Default is <see langword="true" />.
-        /// </summary>
+        /// </value>
         [Argument(ArgumentType.AtMostOnce, HelpText = "Do averaging?", ShortName = "avg")]
         public bool Averaged = true;
 
@@ -83,7 +99,7 @@ public abstract class AveragedLinearArguments : OnlineLinearArguments
         /// The inexactness tolerance for averaging.
         /// </summary>
         [Argument(ArgumentType.AtMostOnce, HelpText = "The inexactness tolerance for averaging", ShortName = "avgtol")]
-        public float AveragedTolerance = (float)1e-2;
+        internal float AveragedTolerance = (float)1e-2;
 
         [BestFriend]
         internal class AveragedDefaultArgs : OnlineDefaultArgs
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
index ac72e2aceb..d3dbdf619e 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/AveragedPerceptron.cs
@@ -26,8 +26,10 @@ namespace Microsoft.ML.Trainers.Online
 {
     /// <summary>
     /// This is averaged perceptron trainer.
-    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
     /// </summary>
+    /// <remarks>
+    /// For usage details, please see <see cref="StandardLearnersCatalog.AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
+    /// </remarks>
     public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPredictionTransformer<LinearBinaryModelParameters>, LinearBinaryModelParameters>
     {
         public const string LoadNameValue = "AveragedPerceptron";
@@ -40,7 +42,7 @@ public sealed class AveragedPerceptronTrainer : AveragedLinearTrainer<BinaryPred
         public sealed class Options : AveragedLinearArguments
         {
             /// <summary>
-            /// The custom <a href="tmpurl_loss">loss</a>. Default is hinge loss.
+            /// The custom <a href="tmpurl_loss">loss</a>.
             /// </summary>
             [Argument(ArgumentType.Multiple, HelpText = "Loss Function", ShortName = "loss", SortOrder = 50)]
             public ISupportClassificationLossFactory LossFunction = new HingeLoss.Arguments();
@@ -107,9 +109,9 @@ internal AveragedPerceptronTrainer(IHostEnvironment env, Options options)
         /// <param name="featureColumn">The name of the feature column.</param>
         /// <param name="weights">The optional name of the weights column.</param>
         /// <param name="learningRate">The learning rate. </param>
-        /// <param name="decreaseLearningRate">Wheather to decrease learning rate as iterations progress.</param>
+        /// <param name="decreaseLearningRate">Whether to decrease learning rate as iterations progress.</param>
         /// <param name="l2RegularizerWeight">L2 Regularization Weight.</param>
-        /// <param name="numIterations">The number of training iteraitons.</param>
+        /// <param name="numIterations">The number of training iterations.</param>
         internal AveragedPerceptronTrainer(IHostEnvironment env,
             string labelColumn = DefaultColumnNames.Label,
             string featureColumn = DefaultColumnNames.Features,
@@ -127,7 +129,7 @@ internal AveragedPerceptronTrainer(IHostEnvironment env,
                 LearningRate = learningRate,
                 DecreaseLearningRate = decreaseLearningRate,
                 L2RegularizerWeight = l2RegularizerWeight,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
                 LossFunction = new TrivialFactory(lossFunction ?? new HingeLoss())
             })
         {
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
index e1382b3038..2cd75e623c 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/LinearSvm.cs
@@ -239,7 +239,7 @@ internal LinearSvmTrainer(IHostEnvironment env,
                 LabelColumn = labelColumn,
                 FeatureColumn = featureColumn,
                 InitialWeights = weightsColumn,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
             })
         {
         }
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
index 8982127170..b683aefb07 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineGradientDescent.cs
@@ -114,7 +114,7 @@ internal OnlineGradientDescentTrainer(IHostEnvironment env,
                 LearningRate = learningRate,
                 DecreaseLearningRate = decreaseLearningRate,
                 L2RegularizerWeight = l2RegularizerWeight,
-                NumIterations = numIterations,
+                NumberOfIterations = numIterations,
                 LabelColumn = labelColumn,
                 FeatureColumn = featureColumn,
                 InitialWeights = weightsColumn,
diff --git a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
index a1ff85ae01..4dbdc8da57 100644
--- a/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardLearners/Standard/Online/OnlineLinear.cs
@@ -25,7 +25,7 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         [Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter", SortOrder = 50)]
         [TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")]
         [TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)]
-        public int NumIterations = OnlineDefaultArgs.NumIterations;
+        public int NumberOfIterations = OnlineDefaultArgs.NumIterations;
 
         /// <summary>
         /// Initial weights and bias, comma-separated.
@@ -35,12 +35,16 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         public string InitialWeights;
 
         /// <summary>
-        /// Initial weights scale.
+        /// Initial weights and bias scale.
         /// </summary>
-        [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts", SortOrder = 140)]
+        /// <value>
+        /// This property is only used if the provided value is positive and <see cref="InitialWeights"/> is not specified.
+        /// The weights and bias will be randomly selected from InitialWeights * [-0.5,0.5] interval with uniform distribution.
+        /// </value>
+        [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts, initWtsDiameter", SortOrder = 140)]
         [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
         [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
-        public float InitWtsDiameter = 0;
+        public float InitialWeightsDiameter = 0;
 
         /// <summary>
         /// <see langword="true" /> to shuffle data for each training iteration; otherwise, <see langword="false" />.
@@ -50,12 +54,6 @@ public abstract class OnlineLinearArguments : LearnerInputBaseWithLabel
         [TlcModule.SweepableDiscreteParamAttribute("Shuffle", new object[] { false, true })]
         public bool Shuffle = true;
 
-        /// <summary>
-        /// Size of cache when trained in Scope.
-        /// </summary>
-        [Argument(ArgumentType.AtMostOnce, HelpText = "Size of cache when trained in Scope", ShortName = "cache")]
-        public int StreamingCacheSize = 1000000;
-
         [BestFriend]
         internal class OnlineDefaultArgs
         {
@@ -150,13 +148,13 @@ protected TrainStateBase(IChannel ch, int numFeatures, LinearModelParameters pre
                     Weights = new VBuffer<float>(numFeatures, weightValues);
                     Bias = float.Parse(weightStr[numFeatures], CultureInfo.InvariantCulture);
                 }
-                else if (parent.Args.InitWtsDiameter > 0)
+                else if (parent.Args.InitialWeightsDiameter > 0)
                 {
                     var weightValues = new float[numFeatures];
                     for (int i = 0; i < numFeatures; i++)
-                        weightValues[i] = parent.Args.InitWtsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
+                        weightValues[i] = parent.Args.InitialWeightsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
                     Weights = new VBuffer<float>(numFeatures, weightValues);
-                    Bias = parent.Args.InitWtsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
+                    Bias = parent.Args.InitialWeightsDiameter * (parent.Host.Rand.NextSingle() - (float)0.5);
                 }
                 else if (numFeatures <= 1000)
                     Weights = VBufferUtils.CreateDense<float>(numFeatures);
@@ -254,9 +252,8 @@ private protected OnlineLinearTrainer(OnlineLinearArguments args, IHostEnvironme
             : base(Contracts.CheckRef(env, nameof(env)).Register(name), TrainerUtils.MakeR4VecFeature(args.FeatureColumn), label, TrainerUtils.MakeR4ScalarWeightColumn(args.InitialWeights))
         {
             Contracts.CheckValue(args, nameof(args));
-            Contracts.CheckUserArg(args.NumIterations > 0, nameof(args.NumIterations), UserErrorPositive);
-            Contracts.CheckUserArg(args.InitWtsDiameter >= 0, nameof(args.InitWtsDiameter), UserErrorNonNegative);
-            Contracts.CheckUserArg(args.StreamingCacheSize > 0, nameof(args.StreamingCacheSize), UserErrorPositive);
+            Contracts.CheckUserArg(args.NumberOfIterations > 0, nameof(args.NumberOfIterations), UserErrorPositive);
+            Contracts.CheckUserArg(args.InitialWeightsDiameter >= 0, nameof(args.InitialWeightsDiameter), UserErrorNonNegative);
 
             Args = args;
             Name = name;
@@ -315,7 +312,7 @@ private void TrainCore(IChannel ch, RoleMappedData data, TrainStateBase state)
 
             var cursorFactory = new FloatLabelCursor.Factory(data, cursorOpt);
             long numBad = 0;
-            while (state.Iteration < Args.NumIterations)
+            while (state.Iteration < Args.NumberOfIterations)
             {
                 state.BeginIteration(ch);
 
@@ -333,7 +330,7 @@ private void TrainCore(IChannel ch, RoleMappedData data, TrainStateBase state)
             {
                 ch.Warning(
                     "Skipped {0} instances with missing features during training (over {1} iterations; {2} inst/iter)",
-                    numBad, Args.NumIterations, numBad / Args.NumIterations);
+                    numBad, Args.NumberOfIterations, numBad / Args.NumberOfIterations);
             }
         }
 
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index f614c4098c..047ea48440 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -193,11 +193,12 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// Predict a target using a linear binary classification model trained with averaged perceptron trainer.
         /// </summary>
         /// <remarks>
-        /// Perceptron is a classification algorithm that makes its predictions based on a linear function.
-        /// For instance with feature values f0, f1,..., f_D-1, the prediction is given by the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
+        /// Perceptron is a classification algorithm that makes its predictions by finding a separating hyperplane.
+        /// For instance, with feature values f0, f1,..., f_D-1, the prediction is given by determining what side of the hyperplane the point falls into.
+        /// That is the same as the sign of sigma[0, D-1] (w_i * f_i), where w_0, w_1,..., w_D-1 are the weights computed by the algorithm.
         ///
-        /// Perceptron is an online algorithm, i.e., it processes the instances in the training set one at a time.
-        /// The weights are initialized to be 0, or some random values. Then, for each example in the training set, the value of sigma[0, D-1] (w_i * f_i) is computed.
+        /// The perceptron is an online algorithm, which means it processes the instances in the training set one at a time.
+        /// It starts with a set of initial weights (zero, random, or initialized from a previous learner). Then, for each example in the training set, the weighted sum of the features (sigma[0, D-1] (w_i * f_i)) is computed.
         /// If this value has the same sign as the label of the current example, the weights remain the same.If they have opposite signs,
         /// the weights vector is updated by either subtracting or adding (if the label is negative or positive, respectively) the feature vector of the current example,
         /// multiplied by a factor 0 &lt; a &lt;= 1, called the learning rate.In a generalization of this algorithm, the weights are updated by adding the feature vector multiplied by the learning rate,
@@ -217,7 +218,7 @@ public static SdcaMultiClassTrainer StochasticDualCoordinateAscent(this Multicla
         /// <param name="weights">The optional example weights.</param>
         /// <param name="learningRate"><a href="tmpurl_lr">Learning rate</a>.</param>
         /// <param name="decreaseLearningRate">
-        /// <see langword="true" /> to decrease the <a href="tmpurl_calib">learning rate</a> as iterations progress; otherwise, <see langword="false" />.
+        /// <see langword="true" /> to decrease the <paramref name="learningRate"/> as iterations progress; otherwise, <see langword="false" />.
         /// Default is <see langword="false" />.
         /// </param>
         /// <param name="l2RegularizerWeight">L2 weight for <a href='tmpurl_regularization'>regularization</a>.</param>
@@ -251,7 +252,7 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         /// For usage details, please see <see cref="AveragedPerceptron(BinaryClassificationCatalog.BinaryClassificationTrainers, string, string, string, IClassificationLoss, float, bool, float, int)"/>
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
-        /// <param name="options">Advanced trainer options.</param>
+        /// <param name="options">Trainer options.</param>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {
diff --git a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
index 464fd0dc59..f509c71908 100644
--- a/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
+++ b/test/Microsoft.ML.Predictor.Tests/TestPredictors.cs
@@ -744,7 +744,7 @@ public void TestEnsembleCombiner()
                 {
                     FeatureColumn = "Features",
                     LabelColumn = DefaultColumnNames.Label,
-                    NumIterations = 2,
+                    NumberOfIterations = 2,
                     TrainingData = dataView,
                     NormalizeFeatures = NormalizeOption.No
                 }).PredictorModel,
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
index 4b7fdbfaff..ac54587b65 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/TestApi.cs
@@ -182,7 +182,7 @@ public void TrainAveragedPerceptronWithCache()
             var cached = mlContext.Data.Cache(xf);
 
             var estimator = mlContext.BinaryClassification.Trainers.AveragedPerceptron(
-                new AveragedPerceptronTrainer.Options { NumIterations = 2 });
+                new AveragedPerceptronTrainer.Options { NumberOfIterations = 2 });
 
             estimator.Fit(cached).Transform(cached);
 
diff --git a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
index 1ef0cda99c..9954669bd3 100644
--- a/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/OvaTest.cs
@@ -131,7 +131,7 @@ public void OvaLinearSvm()
 
             // Pipeline
             var pipeline = mlContext.MulticlassClassification.Trainers.OneVersusAll(
-                mlContext.BinaryClassification.Trainers.LinearSupportVectorMachines(new LinearSvmTrainer.Options { NumIterations = 100 }),
+                mlContext.BinaryClassification.Trainers.LinearSupportVectorMachines(new LinearSvmTrainer.Options { NumberOfIterations = 100 }),
                 useProbabilities: false);
 
             var model = pipeline.Fit(data);

From 6b5606547497b3bb4a5e81a55523c9e4a41557a9 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 11:47:15 -0800
Subject: [PATCH 21/23] Added sample for the second overload with trainer
 options.

---
 .../AveragedPerceptronWithOptions.cs          | 58 +++++++++++++++++++
 .../StandardLearnersCatalog.cs                |  7 +++
 2 files changed, 65 insertions(+)
 create mode 100644 docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
new file mode 100644
index 0000000000..eaf8066398
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
@@ -0,0 +1,58 @@
+using Microsoft.ML;
+using Microsoft.ML.Trainers.Online;
+
+namespace Microsoft.ML.Samples.Dynamic.Trainers.BinaryClassification
+{
+    public static class AveragedPerceptronWithOptions
+    {
+        public static void Example()
+        {
+            // In this examples we will use the adult income dataset. The goal is to predict
+            // if a person's income is above $50K or not, based on different pieces of information about that person.
+            // For more details about this dataset, please see https://archive.ics.uci.edu/ml/datasets/adult
+
+            // Create a new context for ML.NET operations. It can be used for exception tracking and logging, 
+            // as a catalog of available operations and as the source of randomness.
+            // Setting the seed to a fixed number in this example to make outputs deterministic.
+            var mlContext = new MLContext(seed: 0);
+
+            // Download and featurize the dataset
+            var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
+
+            // Leave out 10% of data for testing
+            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+
+            // Define the trainer options
+            var options = new AveragedPerceptronTrainer.Options()
+            {
+                LossFunction = new SmoothedHingeLoss.Arguments(),
+                LearningRate = 0.1f,
+                DoLazyUpdates = false,
+                RecencyGain = 0.1f,
+                NumberOfIterations = 10,
+                LabelColumn = "IsOver50K",
+                FeatureColumn = "Features"
+            };
+
+            // Create data training pipeline
+            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron(options);
+
+            // Fit this pipeline to the training data
+            var model = pipeline.Fit(trainData);
+
+            // Evaluate how the model is doing on the test data
+            var dataWithPredictions = model.Transform(testData);
+            var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
+            SamplesUtils.ConsoleUtils.PrintMetrics(metrics);
+
+            // Output:
+            // Accuracy: 0.86
+            // AUC: 0.90
+            // F1 Score: 0.66
+            // Negative Precision: 0.89
+            // Negative Recall: 0.93
+            // Positive Precision: 0.72
+            // Positive Recall: 0.61
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
index 047ea48440..d78464acde 100644
--- a/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
+++ b/src/Microsoft.ML.StandardLearners/StandardLearnersCatalog.cs
@@ -253,6 +253,13 @@ public static AveragedPerceptronTrainer AveragedPerceptron(
         /// </summary>
         /// <param name="catalog">The binary classification catalog trainer object.</param>
         /// <param name="options">Trainer options.</param>
+        /// <example>
+        /// <format type="text/markdown">
+        /// <![CDATA[
+        /// [!code-csharp[AveragedPerceptron](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/BinaryClassification/AveragedPerceptronWithOptions.cs)]
+        /// ]]>
+        /// </format>
+        /// </example>
         public static AveragedPerceptronTrainer AveragedPerceptron(
             this BinaryClassificationCatalog.BinaryClassificationTrainers catalog, AveragedPerceptronTrainer.Options options)
         {

From c48773395579b33500591957ecab58e2beaf4fc5 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 14:20:19 -0800
Subject: [PATCH 22/23] Fixed the failing tests

---
 .../Common/EntryPoints/core_manifest.json     | 57 ++++---------------
 .../UnitTests/TestEntryPoints.cs              |  5 +-
 2 files changed, 14 insertions(+), 48 deletions(-)

diff --git a/test/BaselineOutput/Common/EntryPoints/core_manifest.json b/test/BaselineOutput/Common/EntryPoints/core_manifest.json
index 5154957bf1..5d720ce6de 100644
--- a/test/BaselineOutput/Common/EntryPoints/core_manifest.json
+++ b/test/BaselineOutput/Common/EntryPoints/core_manifest.json
@@ -4306,7 +4306,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -4325,11 +4325,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -4467,18 +4468,6 @@
               true
             ]
           }
-        },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
         }
       ],
       "Outputs": [
@@ -13247,7 +13236,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -13266,11 +13255,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -13353,18 +13343,6 @@
             ]
           }
         },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
-        },
         {
           "Name": "BatchSize",
           "Type": "Int",
@@ -14272,7 +14250,7 @@
           }
         },
         {
-          "Name": "NumIterations",
+          "Name": "NumberOfIterations",
           "Type": "Int",
           "Desc": "Number of iterations",
           "Aliases": [
@@ -14291,11 +14269,12 @@
           }
         },
         {
-          "Name": "InitWtsDiameter",
+          "Name": "InitialWeightsDiameter",
           "Type": "Float",
           "Desc": "Init weights diameter",
           "Aliases": [
-            "initwts"
+            "initwts",
+            "initWtsDiameter"
           ],
           "Required": false,
           "SortOrder": 140.0,
@@ -14410,18 +14389,6 @@
               true
             ]
           }
-        },
-        {
-          "Name": "StreamingCacheSize",
-          "Type": "Int",
-          "Desc": "Size of cache when trained in Scope",
-          "Aliases": [
-            "cache"
-          ],
-          "Required": false,
-          "SortOrder": 150.0,
-          "IsNullable": false,
-          "Default": 1000000
         }
       ],
       "Outputs": [
diff --git a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
index 8b0c46b817..512329a123 100644
--- a/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
+++ b/test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
@@ -5447,11 +5447,10 @@ public void TestOvaMacroWithUncalibratedLearner()
                                         'RecencyGainMulti': false,
                                         'Averaged': true,
                                         'AveragedTolerance': 0.01,
-                                        'NumIterations': 1,
+                                        'NumberOfIterations': 1,
                                         'InitialWeights': null,
-                                        'InitWtsDiameter': 0.0,
+                                        'InitialWeightsDiameter': 0.0,
                                         'Shuffle': false,
-                                        'StreamingCacheSize': 1000000,
                                         'LabelColumn': 'Label',
                                         'TrainingData': '$Var_9ccc8bce4f6540eb8a244ab40585602a',
                                         'FeatureColumn': 'Features',

From 59673b8f5e513768d4d3e83325334fa89f483da8 Mon Sep 17 00:00:00 2001
From: Shahab Moradi <shmoradi@microsoft.com>
Date: Tue, 12 Feb 2019 15:21:15 -0800
Subject: [PATCH 23/23] Fixed breaking changes from master. For sample code,
 changed numIterations to 10 as per Justin's request

---
 .../AveragedPerceptron.cs                     | 21 ++++++++++---------
 .../AveragedPerceptronWithOptions.cs          |  6 +++---
 2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
index 35bdefa434..ee2e3fdd94 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptron.cs
@@ -19,27 +19,28 @@ public static void Example()
             var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
 
             // Leave out 10% of data for testing
-            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+            var trainTestData = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
 
             // Create data training pipeline
-            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron("IsOver50K", "Features");
+            var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron(
+                                        "IsOver50K", "Features", numIterations: 10);
 
             // Fit this pipeline to the training data
-            var model = pipeline.Fit(trainData);
+            var model = pipeline.Fit(trainTestData.TrainSet);
 
             // Evaluate how the model is doing on the test data
-            var dataWithPredictions = model.Transform(testData);
+            var dataWithPredictions = model.Transform(trainTestData.TestSet);
             var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
             SamplesUtils.ConsoleUtils.PrintMetrics(metrics);
 
             // Output:
-            // Accuracy: 0.85
-            // AUC: 0.90
-            // F1 Score: 0.66
-            // Negative Precision: 0.89
+            // Accuracy: 0.86
+            // AUC: 0.91
+            // F1 Score: 0.68
+            // Negative Precision: 0.90
             // Negative Recall: 0.91
-            // Positive Precision: 0.69
-            // Positive Recall: 0.63
+            // Positive Precision: 0.70
+            // Positive Recall: 0.66
         }
     }
 }
\ No newline at end of file
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
index eaf8066398..ac9296a96d 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/BinaryClassification/AveragedPerceptronWithOptions.cs
@@ -20,7 +20,7 @@ public static void Example()
             var data = SamplesUtils.DatasetUtils.LoadFeaturizedAdultDataset(mlContext);
 
             // Leave out 10% of data for testing
-            var (trainData, testData) = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
+            var trainTestData = mlContext.BinaryClassification.TrainTestSplit(data, testFraction: 0.1);
 
             // Define the trainer options
             var options = new AveragedPerceptronTrainer.Options()
@@ -38,10 +38,10 @@ public static void Example()
             var pipeline = mlContext.BinaryClassification.Trainers.AveragedPerceptron(options);
 
             // Fit this pipeline to the training data
-            var model = pipeline.Fit(trainData);
+            var model = pipeline.Fit(trainTestData.TrainSet);
 
             // Evaluate how the model is doing on the test data
-            var dataWithPredictions = model.Transform(testData);
+            var dataWithPredictions = model.Transform(trainTestData.TestSet);
             var metrics = mlContext.BinaryClassification.EvaluateNonCalibrated(dataWithPredictions, "IsOver50K");
             SamplesUtils.ConsoleUtils.PrintMetrics(metrics);