示例#1
0
        public static MLBBaseballBatter FromCsv(string csvLine)
        {
            string[]          values            = csvLine.Split(',');
            MLBBaseballBatter mlbBaseballBatter = new MLBBaseballBatter();

            mlbBaseballBatter.InductedToHallOfFame = Convert.ToBoolean(values[0]);
            mlbBaseballBatter.OnHallOfFameBallot   = Convert.ToBoolean(values[1]);
            mlbBaseballBatter.FullPlayerName       = Convert.ToString(values[2]);
            mlbBaseballBatter.YearsPlayed          = float.Parse(values[3], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.AB                 = float.Parse(values[4], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.R                  = float.Parse(values[5], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.H                  = float.Parse(values[6], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.Doubles            = float.Parse(values[7], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.Triples            = float.Parse(values[8], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.HR                 = float.Parse(values[9], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.RBI                = float.Parse(values[10], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.SB                 = float.Parse(values[11], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.BattingAverage     = float.Parse(values[12], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.SluggingPct        = float.Parse(values[13], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.AllStarAppearances = float.Parse(values[14]);
            //mlbBaseballBatter.MVPs = float.Parse(values[15], System.Globalization.NumberStyles.Any);
            //mlbBaseballBatter.TripleCrowns = float.Parse(values[16], System.Globalization.NumberStyles.Any);
            //mlbBaseballBatter.GoldGloves = float.Parse(values[17], System.Globalization.NumberStyles.Any);
            //mlbBaseballBatter.MajorLeaguePlayerOfTheYearAwards = float.Parse(values[18], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.TB = float.Parse(values[15], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.TotalPlayerAwards = float.Parse(values[16], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.LastYearPlayed    = float.Parse(values[17], System.Globalization.NumberStyles.Any);
            mlbBaseballBatter.ID = Convert.ToString(values[18]);

            return(mlbBaseballBatter);
        }
        static void Main(string[] args)
        {
            // Start stopwatch to time model job
            Stopwatch sw = new Stopwatch();

            sw.Start();

            Console.Title           = "Baseball Predictions - Training Model Job";
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("Starting Baseball Predictions - Training Model Job");
            Console.WriteLine("Using ML.NET - Version 1.4");
            Console.WriteLine();
            Console.ResetColor();
            Console.WriteLine("This job will build a series of models that will predict both:");
            Console.WriteLine("1) Whether a baseball batter would make it on the HOF Ballot (OnHallOfFameBallot)");
            Console.WriteLine("2) Whether a baseball batter would be inducted to the HOF (InductedToHallOfFame).");
            Console.WriteLine("Based on an MLB batter's summarized career batting statistics.\n");
            Console.WriteLine("Note: The goal is to build a 'good enough' set of models & showcase the ML.NET framework.");
            Console.WriteLine("Note: For better models advanced historical scaling and features should be performed.");
            Console.WriteLine();

            #region Step 1) ML.NET Setup & Load Data

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 1: Load Data from files...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Set the seed explicitly for reproducability (models will be built with consistent results)
            _mlContext = new MLContext(seed: 200);

            // Read the training/validation data from a text file
            var dataTrain = _mlContext.Data.LoadFromTextFile <MLBBaseballBatter>(path: _trainDataPath,
                                                                                 hasHeader: true, separatorChar: ',', allowQuoting: false);
            var dataValidation = _mlContext.Data.LoadFromTextFile <MLBBaseballBatter>(path: _validationDataPath,
                                                                                      hasHeader: true, separatorChar: ',', allowQuoting: false);

            // Retrieve Data Schema
            var dataSchema = dataTrain.Schema;

            #if DEBUG
            // Debug Only: Preview the training/validation data
            var dataTrainPreview      = dataTrain.Preview();
            var dataValidationPreview = dataValidation.Preview();
            #endif

            // Cache the loaded data
            var cachedTrainData      = _mlContext.Data.Cache(dataTrain);
            var cachedValidationData = _mlContext.Data.Cache(dataValidation);

            #endregion

            #region Step 2) Build Multiple Machine Learning Models

            // Notes:
            // Model training is for demo purposes and uses the default hyperparameters.
            // Default parameters were used in optimizing for large data sets.
            // It is best practice to always provide hyperparameters explicitly in order to have historical reproducability
            // as the ML.NET API evolves.

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 2: Train Models...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            /* LIGHTGBM MODELS */
            Console.WriteLine("Training...LightGbm Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineLightGbmOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelLightGbmOnHallOfFameBallot = learningPipelineLightGbmOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineLightGbmInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelLightGbmInductedToHallOfFame = learningPipelineLightGbmInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFame, _mlContext, cachedTrainData);


            /* LOGISTIC REGRESSION MODELS */
            Console.WriteLine("Training...Logistic Regression Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineLogisticRegressionOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelLogisticRegressionOnHallOfFameBallot = learningPipelineLogisticRegressionOnHallOfFameBallot.Fit(cachedTrainData);

            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LogisticRegression", _labelColunmn, modelLogisticRegressionOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "LogisticRegression", _labelColunmn, modelLogisticRegressionOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineLogisticRegressionInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelLogisticRegressionInductedToHallOfFame = learningPipelineLogisticRegressionInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LogisticRegression", _labelColunmn, modelLogisticRegressionInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "LogisticRegression", _labelColunmn, modelLogisticRegressionInductedToHallOfFame, _mlContext, cachedTrainData);


            /* AVERAGED PERCEPTRON MODELS */
            Console.WriteLine("Training...Averaged Perceptron Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineAveragedPerceptronOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.AveragedPerceptron(labelColumnName: _labelColunmn, numberOfIterations: 10)
                    );
            // Fit (build a Machine Learning Model)
            var modelAveragedPerceptronOnHallOfFameBallot = learningPipelineAveragedPerceptronOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineAveragedPerceptronInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.AveragedPerceptron(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelAveragedPerceptronInductedToHallOfFame = learningPipelineAveragedPerceptronInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronInductedToHallOfFame, _mlContext, cachedTrainData);


            /* FAST FOREST MODELS */
            Console.WriteLine("Training...Fast Forest Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineFastForestOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FastForest(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelFastForestOnHallOfFameBallot = learningPipelineFastForestOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineFastForestInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FastForest(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelFastForestInductedToHallOfFame = learningPipelineFastForestInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFame, _mlContext, cachedTrainData);


            /* FAST TREE MODELS */
            Console.WriteLine("Training...Fast Tree Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineFastTreeOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FastTree(labelColumnName: _labelColunmn, learningRate: 0.01, numberOfTrees: 500)
                    );
            // Fit (build a Machine Learning Model)
            var modelFastTreeOnHallOfFameBallot = learningPipelineFastTreeOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineFastTreeInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FastTree(labelColumnName: _labelColunmn, learningRate: 0.01, numberOfTrees: 500)
                    );
            // Fit (build a Machine Learning Model)
            var modelFastTreeInductedToHallOfFame = learningPipelineFastTreeInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFame, _mlContext, cachedTrainData);


            /* FIELD AWARE FACTORIZATION MODELS */
            Console.WriteLine("Training...Field Aware Factorization Models.");
            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineFieldAwareFactorizationOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FieldAwareFactorizationMachine(featureColumnNames: new[] { "Features" }, labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelFieldAwareFactorizationOnHallOfFameBallot = learningPipelineFieldAwareFactorizationOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineFieldAwareFactorizationInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.FieldAwareFactorizationMachine(featureColumnNames: new[] { "Features" }, labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelFieldAwareFactorizationInductedToHallOfFame = learningPipelineFieldAwareFactorizationInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationInductedToHallOfFame, _mlContext, cachedTrainData);


            /* STOCHASTIC GRADIENT DESCENT - CALIBRATED MODELS */
            Console.WriteLine("Training...Stochastic Gradient Descent - Calibrated Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineStochasticGradientDescentCalibratedOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.SgdCalibrated(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelStochasticGradientDescentCalibratedOnHallOfFameBallot = learningPipelineStochasticGradientDescentCalibratedOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineStochasticGradientDescentCalibratedInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.SgdCalibrated(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelStochasticGradientDescentCalibratedInductedToHallOfFame = learningPipelineStochasticGradientDescentCalibratedInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedInductedToHallOfFame, _mlContext, cachedTrainData);

            /* STOCHASTIC GRADIENT DESCENT - NON CALIBRATED MODELS */
            Console.WriteLine("Training...Stochastic Gradient Descent - NonCalibrated Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineStochasticGradientDescentNonCalibratedOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.SgdNonCalibrated(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot = learningPipelineStochasticGradientDescentNonCalibratedOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineStochasticGradientDescentNonCalibratedInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.SgdNonCalibrated(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelStochasticGradientDescentNonCalibratedInductedToHallOfFame = learningPipelineStochasticGradientDescentNonCalibratedInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedInductedToHallOfFame, _mlContext, cachedTrainData);


            // TODO: Fix for PFI
            //var transformedData = modelStochasticGradientDescentInductedToHallOfFame.Transform(cachedTrainData);
            //var permutationFeatureImportance =
            //_mlContext.BinaryClassification.PermutationFeatureImportance(modelStochasticGradientDescentInductedToHallOfFame.LastTransformer,
            //data: transformedData, labelColumnName: _labelColunmn);
            //Microsoft.ML.Data.TransformerChain<Microsoft.ML.Data.BinaryPredictionTransformer<Microsoft.ML.Calibrators.CalibratedModelParametersBase<Microsoft.ML.Trainers.LinearBinaryModelParameters, Microsoft.ML.Calibrators.PlattCalibrator>>> test;
            //test = modelStochasticGradientDescentInductedToHallOfFame;
            //test = null;
            //var loadedModelTest = Utilities.LoadModel(_mlContext,
            //    Utilities.GetModelPath(_appPath, algorithmName: algorithmsForModelExplainability[0], isOnnx: false, label: _labelColunmn));


            /* GENERALIZED ADDITIVE MODELS */
            Console.WriteLine("Training...Generalized Additive Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.Gam(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelGeneralizedAdditiveModelsOnHallOfFameBallot = learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineGeneralizedAdditiveModelsInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.Gam(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelGeneralizedAdditiveModelsInductedToHallOfFame = learningPipelineGeneralizedAdditiveModelsInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFame, _mlContext, cachedTrainData);


            /* LINEAR SUPPORT VECTOR MODELS */
            Console.WriteLine("Training...Linear Support Vector Models.");

            _labelColunmn = "OnHallOfFameBallot";
            // Build simple data pipeline
            var learningPipelineLinearSupportVectorMachinesOnHallOfFameBallot =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LinearSvm(labelColumnName: _labelColunmn, numberOfIterations: 10)
                    );
            // Fit (build a Machine Learning Model)
            var modelLinearSupportVectorMachinesOnHallOfFameBallot = learningPipelineLinearSupportVectorMachinesOnHallOfFameBallot.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesOnHallOfFameBallot);
            Utilities.SaveOnnxModel(_appPath, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesOnHallOfFameBallot, _mlContext, cachedTrainData);

            _labelColunmn = "InductedToHallOfFame";
            // Build simple data pipeline
            var learningPipelineLinearSupportVectorMachinesInductedToHallOfFame =
                Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
                    _mlContext.BinaryClassification.Trainers.LinearSvm(labelColumnName: _labelColunmn)
                    );
            // Fit (build a Machine Learning Model)
            var modelLinearSupportVectorMachinesInductedToHallOfFame = learningPipelineLinearSupportVectorMachinesInductedToHallOfFame.Fit(cachedTrainData);
            // Save the model to storage
            Utilities.SaveModel(_appPath, _mlContext, dataSchema, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesInductedToHallOfFame);
            Utilities.SaveOnnxModel(_appPath, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesInductedToHallOfFame, _mlContext, cachedTrainData);


            //var test = _mlContext.BinaryClassification.CrossValidate(cachedTrainData, learningPipelineLightGbmInductedToHallOfFame, 100,
            //    labelColumn: _labelColunmn, stratificationColumn: _labelColunmn);

            Console.WriteLine(string.Empty);

            #endregion

            #region Step 3) Report Performance Metrics

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 3: Report Metrics...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            for (int i = 0; i < algorithmsForModelExplainability.Length; i++)
            {
                for (int j = 0; j < labelColumns.Length; j++)
                {
                    var binaryClassificationMetrics = Utilities.GetBinaryClassificationModelMetrics(_appPath, _mlContext, labelColumns[j], algorithmsForModelExplainability[i], cachedValidationData);

                    Console.WriteLine("Evaluation Metrics for " + algorithmsForModelExplainability[i] + " | " + labelColumns[j]);
                    Console.WriteLine("**************************");
                    Console.WriteLine("F1 Score:                 " + Math.Round(binaryClassificationMetrics.F1Score, 4).ToString());
                    Console.WriteLine("AUC - ROC Score:          " + Math.Round(binaryClassificationMetrics.AreaUnderRocCurve, 4).ToString());
                    Console.WriteLine("AUC - Prec/Recall Score:  " + Math.Round(binaryClassificationMetrics.AreaUnderPrecisionRecallCurve, 4).ToString());
                    Console.WriteLine("Precision:                " + Math.Round(binaryClassificationMetrics.PositivePrecision, 4).ToString());
                    Console.WriteLine("Recall:                   " + Math.Round(binaryClassificationMetrics.PositiveRecall, 4).ToString());
                    Console.WriteLine("Accuracy:                 " + Math.Round(binaryClassificationMetrics.Accuracy, 4).ToString());
                    Console.WriteLine("LogLoss:                  " + Math.Round(binaryClassificationMetrics.LogLoss, 4).ToString());
                    Console.WriteLine("**************************");

                    var loadedModel          = Utilities.LoadModel(_mlContext, Utilities.GetModelPath(_appPath, algorithmName: algorithmsForModelExplainability[i], isOnnx: false, label: labelColumns[j]));
                    var transformedModelData = loadedModel.Transform(cachedValidationData);

                    ITransformer lModel = loadedModel;
                    //_mlContext.BinaryClassification.PermutationFeatureImportance(lModel, transformedModelData);

                    var lastTran = loadedModel.LastTransformer;
                    //var enumerator = lastTran.GetEnumerator();

                    // TODO: Check for PFI support
                    ISingleFeaturePredictionTransformer <ModelParametersBase <float> > transfomerForPfi = null; // lastTran;
                    //   (ISingleFeaturePredictionTransformer<ModelParametersBase<float>>) lastTran;


                    //_mlContext.BinaryClassification.PermutationFeatureImportance(modelStochasticGradientDescentInductedToHallOfFame.LastTransformer, data: transformedData, labelColumnName: _labelColunmn);
                    //_mlContext.BinaryClassification.PermutationFeatureImportance(lastTran, data: cachedTrainData, labelColumnName: labelColumns[j]);

                    //if (transfomerForPfi != null)
                    //{
                    //    _mlContext.BinaryClassification.PermutationFeatureImportance(transfomerForPfi, transformedModelData);
                    //}


                    //ISingleFeaturePredictionTransformer<IPredictorProducing<float>> transfomerForPfi = null;
                    //while (enumerator.MoveNext())
                    //{
                    //    if (enumerator.Current is IPredictionTransformer<ModelParametersBase<float>>)
                    //    {
                    //        transfomerForPfi = enumerator.Current as ISingleFeaturePredictionTransformer<ModelParametersBase<float>>;
                    //    }
                    //}

                    if (transfomerForPfi != null)
                    {
                        // Console.WriteLine("!!!!!!!!HEELLO");
                        //_mlContext.BinaryClassification.PermutationFeatureImportance(loadedModel.LastTransformer, null);
                        //// TODO: FIX
                        //// Retrieve Top Features based on Permutation Feature Importance
                        //var permutationMetrics = _mlContext.BinaryClassification.PermutationFeatureImportance(model: loadedModel.LastTransformer, data: transformedModelData,
                        // label: labelColumns[j], features: "Features", useFeatureWeightFilter: false, permutationCount: 10);

                        //// Build a list of feature importance metrics
                        //List<FeatureImportanceValue> featureImportanceValues = new List<FeatureImportanceValue>();
                        //for (int k = 0; k < permutationMetrics.Length; k++)
                        //{
                        //    featureImportanceValues.Add(
                        //            new FeatureImportanceValue
                        //            {
                        //                FeatureName = featureColumns[k],
                        //                PerformanceMetricName = "F1Score.Mean",
                        //                PerformanceMetricValue = permutationMetrics[k].F1Score.Mean
                        //            }
                        //        );
                        //}

                        //// Filter out NaN values and order by lowest values
                        //// Note: Should be done with absolute and check for positive values for features
                        //var orderedFeatures = featureImportanceValues.Where(a => !Double.IsNaN(a.PerformanceMetricValue)).OrderBy(a => a.PerformanceMetricValue).ToList();
                        //var numberOfFeaturesToReport = 4;

                        //Console.WriteLine("Most important features (" + numberOfFeaturesToReport + ")");
                        //Console.WriteLine("******************");

                        //for (int l = 0; l < numberOfFeaturesToReport; l++)
                        //{
                        //    if (l + 1 <= featureImportanceValues.Count && l < orderedFeatures.Count)
                        //    {
                        //        Console.WriteLine(orderedFeatures[l].FeatureName + ": " + Math.Round(orderedFeatures[l].PerformanceMetricValue, 4).ToString());
                        //    }
                        //}
                    }
                    else
                    {
                        // TODO: FIX in post v1.0+

                        //Console.WriteLine("Most important features ()");
                        //Console.WriteLine("******************");
                        //Console.WriteLine("Model's algorithm does not support explainability.");
                    }

                    Console.WriteLine("**************************");
                    Console.WriteLine();
                }
            }



            #endregion

            #region Step 4) New Predictions - Using Ficticious Player Data

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 4: New Predictions...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Set algorithm type to use for predictions
            // Retrieve model path
            // TODO: Hardcoded add perscriptive rules engine
            var algorithmTypeName               = "GeneralizedAdditiveModels";
            var loadedModelOnHallOfFameBallot   = Utilities.LoadModel(_mlContext, (Utilities.GetModelPath(_appPath, algorithmTypeName, false, "OnHallOfFameBallot")));
            var loadedModelInductedToHallOfFame = Utilities.LoadModel(_mlContext, (Utilities.GetModelPath(_appPath, algorithmTypeName, false, "InductedToHallOfFame")));

            // Create prediction engine
            var predEngineOnHallOfFameBallot   = _mlContext.Model.CreatePredictionEngine <MLBBaseballBatter, MLBHOFPrediction>(loadedModelOnHallOfFameBallot);
            var predEngineInductedToHallOfFame = _mlContext.Model.CreatePredictionEngine <MLBBaseballBatter, MLBHOFPrediction>(loadedModelInductedToHallOfFame);

            // Create statistics for bad, average & great player
            var badMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Bad Player",
                ID                   = 100f,
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 2f,
                AB                   = 100f,
                R                  = 10f,
                H                  = 30f,
                Doubles            = 1f,
                Triples            = 1f,
                HR                 = 1f,
                RBI                = 10f,
                SB                 = 10f,
                BattingAverage     = 0.3f,
                SluggingPct        = 0.15f,
                AllStarAppearances = 1f,
                MVPs               = 0f,
                TripleCrowns       = 0f,
                GoldGloves         = 0f,
                MajorLeaguePlayerOfTheYearAwards = 0f,
                TB = 200f
            };
            var averageMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Average Player",
                ID                   = 100f,
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 2f,
                AB                   = 8393f,
                R                  = 1162f,
                H                  = 2340f,
                Doubles            = 410f,
                Triples            = 8f,
                HR                 = 439f,
                RBI                = 1412f,
                SB                 = 9f,
                BattingAverage     = 0.279f,
                SluggingPct        = 0.486f,
                AllStarAppearances = 6f,
                MVPs               = 0f,
                TripleCrowns       = 0f,
                GoldGloves         = 0f,
                MajorLeaguePlayerOfTheYearAwards = 0f,
                TB = 4083f
            };
            var greatMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Great Player",
                ID                   = 100f,
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 20f,
                AB                   = 10000f,
                R                  = 1900f,
                H                  = 3500f,
                Doubles            = 500f,
                Triples            = 150f,
                HR                 = 600f,
                RBI                = 1800f,
                SB                 = 400f,
                BattingAverage     = 0.350f,
                SluggingPct        = 0.65f,
                AllStarAppearances = 14f,
                MVPs               = 2f,
                TripleCrowns       = 1f,
                GoldGloves         = 4f,
                MajorLeaguePlayerOfTheYearAwards = 2f,
                TB = 7000f
            };

            var batters = new List <MLBBaseballBatter> {
                badMLBBatter, averageMLBBatter, greatMLBBatter
            };
            // Convert the list to an IDataView
            var newPredictionsData = _mlContext.Data.LoadFromEnumerable(batters);

            // Make the predictions for both OnHallOfFameBallot & InductedToHallOfFame
            var predBadOnHallOfFameBallot       = predEngineOnHallOfFameBallot.Predict(badMLBBatter);
            var predBadInductedToHallOfFame     = predEngineInductedToHallOfFame.Predict(badMLBBatter);
            var predAverageOnHallOfFameBallot   = predEngineOnHallOfFameBallot.Predict(averageMLBBatter);
            var predAverageInductedToHallOfFame = predEngineInductedToHallOfFame.Predict(averageMLBBatter);
            var predGreatOnHallOfFameBallot     = predEngineOnHallOfFameBallot.Predict(greatMLBBatter);
            var predGreatInductedToHallOfFame   = predEngineInductedToHallOfFame.Predict(greatMLBBatter);

            // Report the results
            Console.WriteLine("Algorithm Used for sample Model Prediction: " + algorithmTypeName);
            Console.WriteLine("\n");
            Console.WriteLine("Bad Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predBadOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predBadOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predBadInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predBadInductedToHallOfFame.Probability);
            Console.WriteLine();
            Console.WriteLine("Average Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predAverageOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predAverageOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predAverageInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predAverageInductedToHallOfFame.Probability);
            Console.WriteLine();
            Console.WriteLine("Great Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predGreatOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predGreatOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predGreatInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predGreatInductedToHallOfFame.Probability);

            #endregion


            // TODO: FINISH

            //var loadedModelPath = GetModelPath("LightGbm", true, "OnHallOfFameBallot");
            //var session = new InferenceSession(loadedModelPath);
            //var inputInfo = session.InputMetadata.First();
            //var outputInfo = session.OutputMetadata.First();

            //VBuffer<float> weights = new VBuffer<float>();
            //modelLogisticRegressionInductedToHallOfFame.LastTransformer.Model.GetFeatureWeights(ref weights);

            //var transformedNewPredictionsData = modelLogisticRegressionInductedToHallOfFame.Transform(newPredictionsData);
            //var explainer = _mlContext.Model.Explainability.FeatureContributionCalculation(modelLogisticRegressionInductedToHallOfFame.LastTransformer.Model);
            //var outputData = explainer.Fit(transformedNewPredictionsData).Transform(transformedNewPredictionsData);

            //var scoringEnumerator = _mlContext.CreateEnumerable<BaseballBatterScoreAndFeatureContribution>(outputData, true).GetEnumerator();

            //int index = 0;
            //Console.WriteLine("Probability\tScore\tBiggestFeature      \t\tValue\tWeight\tContribution");
            //while (scoringEnumerator.MoveNext() && index < 4)
            //{
            //    var row = scoringEnumerator.Current;

            //    // Get the feature index with the biggest contribution
            //    var featureOfInterest = GetMostContributingFeature(row.FeatureContributions);

            //    // And the corresponding information about the feature
            //    var value = row.Features[featureOfInterest];
            //    var contribution = row.FeatureContributions[featureOfInterest];
            //    var name = featureColumns[featureOfInterest];
            //    var weight = weights.GetValues()[featureOfInterest];

            //    Console.WriteLine("{0:0.00}\t{1:0.00}\t\t{2}\t{3:0.00}\t{4:0.00}\t{5:0.00}",
            //        row.Probability,
            //        row.Score,
            //        name,
            //        value,
            //        weight,
            //        contribution
            //        );

            //    index++;
            //}

            // End of job, report time
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(string.Format("Finished Baseball Predictions - Training Model Job in: {0} seconds", Math.Round(sw.Elapsed.TotalSeconds, 2)));
            Console.ReadLine();
        }
        static void Main(string[] args)
        {
            // Start stopwatch to time model job
            Stopwatch sw = new Stopwatch();

            sw.Start();

            Console.Title           = "Baseball Predictions - Training Model Job";
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("Starting Baseball Predictions - Training Model Job");
            Console.WriteLine("Using ML.NET - Version 1.7");
            Console.WriteLine();
            Console.ResetColor();
            Console.WriteLine("This training job will build a series of models that will predict both:");
            Console.WriteLine("1) Whether a baseball batter would make it on the HOF Ballot (OnHallOfFameBallot)");
            Console.WriteLine("2) Whether a baseball batter would be inducted to the HOF (InductedToHallOfFame).");
            Console.WriteLine("Based on an MLB batter's summarized career batting statistics.\n");
            Console.WriteLine("Note: The goal is to build a 'good enough' set of models & showcase the ML.NET framework.");
            Console.WriteLine("Note: For better models advanced historical scaling and features should be performed.");
            Console.WriteLine();

            #region Step 1) ML.NET Setup & Load Data

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 1: Load Data from files...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Set the seed explicitly for reproducability (models will be built with consistent results)
            _mlContext = new MLContext(seed: seed);

            // Read the training/validation data from a text file
            //var dataTrainBatters = File.ReadAllLines(_trainDataPath)
            //    .Skip(1) // Skip the CSV Header
            //    .Select(v => MLBBaseballBatter.FromCsv(v))
            //    .AsQueryable() // Allows for Dyanmic Linq
            //    .Select("new (" + featureColumnsStringArray + ")")
            //    .ToDynamicList();

            var dataTrain = _mlContext.Data.LoadFromTextFile <MLBBaseballBatter>(path: _trainDataPath,
                                                                                 hasHeader: true, separatorChar: ',', allowQuoting: false);
            var dataTest = _mlContext.Data.LoadFromTextFile <MLBBaseballBatter>(path: _testDataPath,
                                                                                hasHeader: true, separatorChar: ',', allowQuoting: false);
            var dataFull = _mlContext.Data.LoadFromTextFile <MLBBaseballBatter>(path: _fullDataPath,
                                                                                hasHeader: true, separatorChar: ',', allowQuoting: false);

            // TODO: REMOVE
            //dynamic myDynamic = new { PropertyOne = true, PropertyTwo = false };
            //var test = myDynamic.GetType();
            //var dynamicList = new List<dynamic>();
            //dynamicList.Add(myDynamic);
            //dynamicList.Add(myDynamic);
            //var test2 = _mlContext.Data.LoadFromEnumerable<dynamic>(dynamicList);
            //var pre = test2.Preview();
            //var testD = dataTrainBatters.FirstOrDefault();
            //Microsoft.ML.Data.SchemaDefinition sd;
            //var schemaDefinition = SchemaDefinition.Create(testD.GetType());
            //var test2 = _mlContext.Data.LoadFromEnumerable<dynamic>(dataTrainBatters, schemaDefinition );
            //var test2preview = test2.Preview();

            // Retrieve Data Schema
            var dataSchema = dataTrain.Schema;

            #if DEBUG
            // Debug Only: Preview the training/test data
            var dataTrainPreview = dataTrain.Preview();
            var dataTestPreview  = dataTest.Preview();
            var dataFullPreview  = dataFull.Preview();
            #endif

            // Cache the loaded data
            var cachedTrainData = _mlContext.Data.Cache(dataTrain);
            var cachedTestData  = _mlContext.Data.Cache(dataTest);
            var cachedFullData  = _mlContext.Data.Cache(dataFull);

            // Delete the Performance Metrics File(s)
            File.Delete(_performanceMetricsTrainTestModels);

            #endregion

            #region Step 2) Build Multiple Machine Learning Models

            // Notes:
            // Model training is for demo purposes and uses the default hyperparameters.
            // Default parameters were used in optimizing for large data sets.
            // It is best practice to always provide hyperparameters explicitly in order to have historical reproducability
            // as the ML.NET API evolves.

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 2: Train Models...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Build list of BaseballBatter Trainers
            var trainers = new List <ITrainerBase>();


            foreach (var labelColumn in labelColumns)
            {
                trainers.Add(new AveragedPerceptronBaseballBatterTrainer(labelColumn));
                trainers.Add(new FastForestBaseballBatterTrainer(labelColumn));
                trainers.Add(new FastTreeBaseballBatterTrainer(labelColumn));
                trainers.Add(new GamBaseballBatterTrainer(labelColumn));
                trainers.Add(new LightGbmBaseballBatterTrainer(labelColumn));
                trainers.Add(new LinearSvmBaseballBatterTrainer(labelColumn));
                trainers.Add(new LbfgsLogisticRegressionBaseballBatterTrainer(labelColumn));
                trainers.Add(new SgdCalibratedBaseballBatterTrainer(labelColumn));
                trainers.Add(new SgdNonCalibratedBaseballBatterTrainer(labelColumn));
            }
            ;

            foreach (var trainer in trainers)
            {
                // Fit a trainer on training data & evaluate performance metrics
                Console.WriteLine($"Training...{trainer.Name} model.");
                trainer.Fit(cachedTrainData);
                var performanceMetrics = trainer.Evaluate(cachedTestData);
                // Save model
                trainer.SaveModel(appFolder, false, cachedTrainData);

                // Fit a trainer on full data & persist final model
                Console.WriteLine($"Training...{trainer.Name} final model.");
                trainer.Fit(cachedTrainData);
                // Save model
                trainer.SaveModel(appFolder, true, cachedFullData);
            }

            ///* LIGHTGBM MODELS */
            //Console.WriteLine("Training...LightGbm Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineLightGbmOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelLightGbmOnHallOfFameBallot = learningPipelineLightGbmOnHallOfFameBallot.Fit(cachedTrainData);
            //var modelLightGbmOnHallOfFameBallotFull = learningPipelineLightGbmOnHallOfFameBallot.Fit(cachedFullData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallot, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallotFull);
            //Utilities.SaveOnnxModel(true, appFolder, "LightGbm", _labelColunmn, modelLightGbmOnHallOfFameBallotFull, _mlContext, cachedFullData);


            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineLightGbmInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: _labelColunmn)
            //    );

            //// Fit (build a Machine Learning Model)
            //var modelLightGbmInductedToHallOfFame = learningPipelineLightGbmInductedToHallOfFame.Fit(cachedTrainData);
            //var modelLightGbmInductedToHallOfFameFull = learningPipelineLightGbmInductedToHallOfFame.Fit(cachedFullData);
            //// Save the models to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFame, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFameFull);
            //Utilities.SaveOnnxModel(true, appFolder, "LightGbm", _labelColunmn, modelLightGbmInductedToHallOfFameFull, _mlContext, cachedFullData);

            ///* LOGISTIC REGRESSION MODELS */
            //Console.WriteLine("Training...Logistic Regression Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineLogisticRegressionOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelLogisticRegressionOnHallOfFameBallot = learningPipelineLogisticRegressionOnHallOfFameBallot.Fit(cachedTrainData);

            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LogisticRegression", _labelColunmn, modelLogisticRegressionOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "LogisticRegression", _labelColunmn, modelLogisticRegressionOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineLogisticRegressionInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LbfgsLogisticRegression(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelLogisticRegressionInductedToHallOfFame = learningPipelineLogisticRegressionInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LogisticRegression", _labelColunmn, modelLogisticRegressionInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "LogisticRegression", _labelColunmn, modelLogisticRegressionInductedToHallOfFame, _mlContext, cachedTrainData);


            ///* AVERAGED PERCEPTRON MODELS */
            //Console.WriteLine("Training...Averaged Perceptron Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineAveragedPerceptronOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.AveragedPerceptron(labelColumnName: _labelColunmn, numberOfIterations: 10)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelAveragedPerceptronOnHallOfFameBallot = learningPipelineAveragedPerceptronOnHallOfFameBallot.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineAveragedPerceptronInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.AveragedPerceptron(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelAveragedPerceptronInductedToHallOfFame = learningPipelineAveragedPerceptronInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "AveragedPerceptron", _labelColunmn, modelAveragedPerceptronInductedToHallOfFame, _mlContext, cachedTrainData);


            ///* FAST FOREST MODELS */
            //Console.WriteLine("Training...Fast Forest Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineFastForestOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FastForest(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFastForestOnHallOfFameBallot = learningPipelineFastForestOnHallOfFameBallot.Fit(cachedTrainData);
            //var modelFastForestOnHallOfFameBallotFull = learningPipelineFastForestOnHallOfFameBallot.Fit(cachedFullData);

            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallot, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallotFull);
            //Utilities.SaveOnnxModel(true, appFolder, "FastForest", _labelColunmn, modelFastForestOnHallOfFameBallotFull, _mlContext, cachedFullData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineFastForestInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FastForest(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFastForestInductedToHallOfFame = learningPipelineFastForestInductedToHallOfFame.Fit(cachedTrainData);
            //var modelFastForestInductedToHallOfFameFull = learningPipelineFastForestInductedToHallOfFame.Fit(cachedFullData);

            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFame, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFameFull);
            //Utilities.SaveOnnxModel(true, appFolder, "FastForest", _labelColunmn, modelFastForestInductedToHallOfFameFull, _mlContext, cachedTrainData);


            ///* FAST TREE MODELS */
            //Console.WriteLine("Training...Fast Tree Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineFastTreeOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FastTree(labelColumnName: _labelColunmn, learningRate: 0.01, numberOfTrees: 500)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFastTreeOnHallOfFameBallot = learningPipelineFastTreeOnHallOfFameBallot.Fit(cachedTrainData);
            //var modelFastTreeOnHallOfFameBallotFull = learningPipelineFastTreeOnHallOfFameBallot.Fit(cachedFullData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallot, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallotFull);
            //Utilities.SaveOnnxModel(true, appFolder, "FastTree", _labelColunmn, modelFastTreeOnHallOfFameBallotFull, _mlContext, cachedFullData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineFastTreeInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FastTree(labelColumnName: _labelColunmn, learningRate: 0.01, numberOfTrees: 500)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFastTreeInductedToHallOfFame = learningPipelineFastTreeInductedToHallOfFame.Fit(cachedTrainData);
            //var modelFastTreeInductedToHallOfFameFull = learningPipelineFastTreeInductedToHallOfFame.Fit(cachedFullData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFame, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFameFull);
            //Utilities.SaveOnnxModel(true, appFolder, "FastTree", _labelColunmn, modelFastTreeInductedToHallOfFameFull, _mlContext, cachedFullData);


            ///* FIELD AWARE FACTORIZATION MODELS */
            //Console.WriteLine("Training...Field Aware Factorization Models.");
            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineFieldAwareFactorizationOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FieldAwareFactorizationMachine(featureColumnNames: new[] { "Features" }, labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFieldAwareFactorizationOnHallOfFameBallot = learningPipelineFieldAwareFactorizationOnHallOfFameBallot.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineFieldAwareFactorizationInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.FieldAwareFactorizationMachine(featureColumnNames: new[] { "Features" }, labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelFieldAwareFactorizationInductedToHallOfFame = learningPipelineFieldAwareFactorizationInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "FieldAwareFactorization", _labelColunmn, modelFieldAwareFactorizationInductedToHallOfFame, _mlContext, cachedTrainData);


            ///* STOCHASTIC GRADIENT DESCENT - CALIBRATED MODELS */
            //Console.WriteLine("Training...Stochastic Gradient Descent - Calibrated Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineStochasticGradientDescentCalibratedOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.SgdCalibrated(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelStochasticGradientDescentCalibratedOnHallOfFameBallot = learningPipelineStochasticGradientDescentCalibratedOnHallOfFameBallot.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineStochasticGradientDescentCalibratedInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.SgdCalibrated(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelStochasticGradientDescentCalibratedInductedToHallOfFame = learningPipelineStochasticGradientDescentCalibratedInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "StochasticGradientDescentCalibrated", _labelColunmn, modelStochasticGradientDescentCalibratedInductedToHallOfFame, _mlContext, cachedTrainData);

            ///* STOCHASTIC GRADIENT DESCENT - NON CALIBRATED MODELS */
            //Console.WriteLine("Training...Stochastic Gradient Descent - NonCalibrated Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineStochasticGradientDescentNonCalibratedOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.SgdNonCalibrated(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot = learningPipelineStochasticGradientDescentNonCalibratedOnHallOfFameBallot.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineStochasticGradientDescentNonCalibratedInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.SgdNonCalibrated(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelStochasticGradientDescentNonCalibratedInductedToHallOfFame = learningPipelineStochasticGradientDescentNonCalibratedInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "StochasticGradientDescentNonCalibrated", _labelColunmn, modelStochasticGradientDescentNonCalibratedInductedToHallOfFame, _mlContext, cachedTrainData);


            ///* GENERALIZED ADDITIVE MODELS */
            //Console.WriteLine("Training...Generalized Additive Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.Gam(labelColumnName: _labelColunmn, numberOfIterations: 10000, learningRate: 0.001, maximumBinCountPerFeature: 500)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelGeneralizedAdditiveModelsOnHallOfFameBallot = learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot.Fit(cachedTrainData);
            //var modelGeneralizedAdditiveModelsOnHallOfFameBallotFull = learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot.Fit(cachedFullData);
            //// Save the models to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallot, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallotFull);
            //Utilities.SaveOnnxModel(true, appFolder, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsOnHallOfFameBallotFull, _mlContext, cachedFullData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineGeneralizedAdditiveModelsInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.Gam(labelColumnName: _labelColunmn, numberOfIterations: 10000, learningRate: 0.001, maximumBinCountPerFeature: 500)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelGeneralizedAdditiveModelsInductedToHallOfFame = learningPipelineGeneralizedAdditiveModelsInductedToHallOfFame.Fit(cachedTrainData);
            //var modelGeneralizedAdditiveModelsInductedToHallOfFameFull = learningPipelineGeneralizedAdditiveModelsInductedToHallOfFame.Fit(cachedFullData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFame, _mlContext, cachedTrainData);
            //Utilities.SaveModel(true, appFolder, _mlContext, dataSchema, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFameFull);
            //Utilities.SaveOnnxModel(true, appFolder, "GeneralizedAdditiveModels", _labelColunmn, modelGeneralizedAdditiveModelsInductedToHallOfFameFull, _mlContext, cachedFullData);


            ///* LINEAR SUPPORT VECTOR MODELS */
            //Console.WriteLine("Training...Linear Support Vector Models.");

            //_labelColunmn = "OnHallOfFameBallot";
            //// Build simple data pipeline
            //var learningPipelineLinearSupportVectorMachinesOnHallOfFameBallot =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LinearSvm(labelColumnName: _labelColunmn, numberOfIterations: 10)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelLinearSupportVectorMachinesOnHallOfFameBallot = learningPipelineLinearSupportVectorMachinesOnHallOfFameBallot.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesOnHallOfFameBallot);
            //Utilities.SaveOnnxModel(false, appFolder, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesOnHallOfFameBallot, _mlContext, cachedTrainData);

            //_labelColunmn = "InductedToHallOfFame";
            //// Build simple data pipeline
            //var learningPipelineLinearSupportVectorMachinesInductedToHallOfFame =
            //    Utilities.GetBaseLinePipeline(_mlContext, featureColumns).Append(
            //    _mlContext.BinaryClassification.Trainers.LinearSvm(labelColumnName: _labelColunmn)
            //    );
            //// Fit (build a Machine Learning Model)
            //var modelLinearSupportVectorMachinesInductedToHallOfFame = learningPipelineLinearSupportVectorMachinesInductedToHallOfFame.Fit(cachedTrainData);
            //// Save the model to storage
            //Utilities.SaveModel(false, appFolder, _mlContext, dataSchema, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesInductedToHallOfFame);
            //Utilities.SaveOnnxModel(false, appFolder, "LinearSupportVectorMachines", _labelColunmn, modelLinearSupportVectorMachinesInductedToHallOfFame, _mlContext, cachedTrainData);


            Console.WriteLine(string.Empty);

            #endregion

            //#region Step 3) Cross-Validate GAM

            //Console.ForegroundColor = ConsoleColor.Yellow;
            //Console.WriteLine("###############################");
            //Console.WriteLine("Step 3: Cross Validate GAM");
            //Console.WriteLine("###############################\n");
            //Console.ResetColor();

            //Console.WriteLine("Cross Validating GeneralizedAdditiveModels");

            //var crossValidationPerformance = _mlContext.BinaryClassification.CrossValidate(cachedFullData, learningPipelineGeneralizedAdditiveModelsOnHallOfFameBallot, 20,
            //    labelColumnName: "OnHallOfFameBallot", seed: seed);

            //Console.WriteLine("Accuracy");
            //crossValidationPerformance.Select(fold => fold.Metrics.Accuracy).ToList().ForEach(i => Console.Write("{0},", Math.Round(i, 4)));
            //var accuracyStdDev = crossValidationPerformance.Select(fold => fold.Metrics.Accuracy).ToList().StandardDeviation();
            //var accuracyMean = crossValidationPerformance.Select(fold => fold.Metrics.Accuracy).ToList().Mean();
            //var accuracyConfidenceRangeLower = Math.Round(accuracyMean - 1.96*accuracyStdDev, 3);
            //var accuracyConfidenceRangeHigher = Math.Round(accuracyMean + 1.96*accuracyStdDev, 3);
            //Console.WriteLine(string.Empty);
            //Console.WriteLine("Accuracy: " + accuracyMean + " 95% CI: " + accuracyConfidenceRangeLower + " - " + accuracyConfidenceRangeHigher);

            //Console.WriteLine("F1Score");
            //crossValidationPerformance.Select(fold => fold.Metrics.F1Score).ToList().ForEach(i => Console.Write("{0},", Math.Round(i, 4)));
            //var f1ScoreStdDev = crossValidationPerformance.Select(fold => fold.Metrics.F1Score).ToList().StandardDeviation();
            //var f1ScoreMean = crossValidationPerformance.Select(fold => fold.Metrics.F1Score).ToList().Mean();
            //var f1ScoreConfidenceRangeLower = Math.Round(f1ScoreMean - 1.96 * f1ScoreStdDev, 3);
            //var f1ScoreConfidenceRangeHigher = Math.Round(f1ScoreMean + 1.96 * f1ScoreStdDev, 3);
            //Console.WriteLine(string.Empty);
            //Console.WriteLine("F1Score: " + f1ScoreMean + "95% CI: " + f1ScoreConfidenceRangeLower + " - " + f1ScoreConfidenceRangeHigher);

            //Console.WriteLine("PositivePrecision");
            //crossValidationPerformance.Select(fold => fold.Metrics.PositivePrecision).ToList().ForEach(i => Console.Write("{0},", Math.Round(i, 4)));
            //var positivePrecisionStdDev = crossValidationPerformance.Select(fold => fold.Metrics.PositivePrecision).ToList().StandardDeviation();
            //var positivePrecisionMean = crossValidationPerformance.Select(fold => fold.Metrics.PositivePrecision).ToList().Mean();
            //var positivePrecisionRangeLower = Math.Round(positivePrecisionMean - 1.96 * positivePrecisionStdDev, 3);
            //var positivePrecisionRangeHigher = Math.Round(positivePrecisionMean + 1.96 * positivePrecisionStdDev, 3);
            //Console.WriteLine(string.Empty);
            //Console.WriteLine("PositivePrecision: " + positivePrecisionMean + " 95% CI: " + positivePrecisionRangeLower + " - " + positivePrecisionRangeHigher);

            //Console.WriteLine("PositiveRecall");
            //crossValidationPerformance.Select(fold => fold.Metrics.PositiveRecall).ToList().ForEach(i => Console.Write("{0},", Math.Round(i, 4)));
            //var positiveRecallStdDev = crossValidationPerformance.Select(fold => fold.Metrics.PositiveRecall).ToList().StandardDeviation();
            //var positiveRecallMean = crossValidationPerformance.Select(fold => fold.Metrics.PositiveRecall).ToList().Mean();
            //var positiveRecallRangeLower = Math.Round(positiveRecallMean - 1.96 * positiveRecallStdDev, 3);
            //var positiveRecallRangeHigher = Math.Round(positiveRecallMean + 1.96 * positiveRecallStdDev, 3);
            //Console.WriteLine(string.Empty);
            //Console.WriteLine("PositiveRecall: " + positiveRecallMean + " 95% CI: " + positiveRecallRangeLower + " - " + positiveRecallRangeHigher);

            //#endregion

            #region Step 4) Report Performance Metrics

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 4: Report Metrics...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Write the performance metrics HEADER
            var performanceMetricsTrainTestHeaderRow = $@"{"AlgorithmName"},{"LabelColumn"},{"Seed"},{"F1Score"},{"AreaUnderPrecisionRecallCurve"},{"AreaUnderRocCurve"},{"PositivePrecision"},{"PositiveRecall"},{"Accuracy"},{"LogLoss"}";
            using (System.IO.StreamWriter file = File.AppendText(_performanceMetricsTrainTestModels))
            {
                file.WriteLine(performanceMetricsTrainTestHeaderRow);
            }

            for (int i = 0; i < algorithmsForModelExplainability.Length; i++)
            {
                for (int j = 0; j < labelColumns.Length; j++)
                {
                    // TRAIN/TEST MODEL PERFORMANCE METRICS
                    var isFinalModel = false;
                    var binaryClassificationMetrics = Utilities.GetBinaryClassificationModelMetrics(isFinalModel, appFolder, _mlContext, labelColumns[j], algorithmsForModelExplainability[i], cachedTestData);

                    var metricF1Score = Math.Round(binaryClassificationMetrics.F1Score, 4);
                    var metricAreaUnderPrecisionRecallCurve = Math.Round(binaryClassificationMetrics.AreaUnderPrecisionRecallCurve, 4);
                    var metricAreaUnderRocCurve             = Math.Round(binaryClassificationMetrics.AreaUnderRocCurve, 4);
                    var metricPositivePrecision             = Math.Round(binaryClassificationMetrics.PositivePrecision, 4);
                    var metricPositiveRecall = Math.Round(binaryClassificationMetrics.PositiveRecall, 4);
                    var metricAccuracy       = Math.Round(binaryClassificationMetrics.Accuracy, 4);
                    var metricLogLoss        = Math.Round(binaryClassificationMetrics.LogLoss, 4);

                    Console.WriteLine("TRAIN/TEST Performance Metrics for " + algorithmsForModelExplainability[i] + " | " + labelColumns[j]);
                    Console.WriteLine("**************************");
                    Console.WriteLine("F1 Score:                 " + metricF1Score);
                    Console.WriteLine("AUC - Prec/Recall Score:  " + metricAreaUnderPrecisionRecallCurve);
                    Console.WriteLine("AUC - ROC Score:          " + metricAreaUnderRocCurve);
                    Console.WriteLine("Precision:                " + metricPositivePrecision);
                    Console.WriteLine("Recall:                   " + metricPositiveRecall);
                    Console.WriteLine("Accuracy:                 " + metricAccuracy);
                    Console.WriteLine("LogLoss:                  " + metricLogLoss);
                    Console.WriteLine("**************************");

                    // Write the performance metrics to file
                    var performanceMetricsTrainTestRow = $@"{algorithmsForModelExplainability[i]},{labelColumns[j]},{seed},{metricF1Score},{metricAreaUnderPrecisionRecallCurve},{metricAreaUnderRocCurve},{metricPositivePrecision},{metricPositiveRecall},{metricAccuracy},{metricLogLoss}";
                    using (System.IO.StreamWriter file = File.AppendText(_performanceMetricsTrainTestModels))
                    {
                        file.WriteLine(performanceMetricsTrainTestRow);
                    }

                    Console.WriteLine("**************************");
                    Console.WriteLine();
                }
            }



            #endregion

            #region Step 5) New Predictions - Using Ficticious Player Data

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("###############################");
            Console.WriteLine("Step 5: New Predictions...");
            Console.WriteLine("###############################\n");
            Console.ResetColor();

            // Set algorithm type to use for predictions
            // Retrieve model path
            // TODO: Hardcoded add perscriptive rules engine
            var algorithmTypeName               = "GeneralizedAdditiveModels";
            var loadedModelOnHallOfFameBallot   = Utilities.LoadModel(_mlContext, (Utilities.GetModelPath(appFolder, algorithmTypeName, false, "OnHallOfFameBallot", true)));
            var loadedModelInductedToHallOfFame = Utilities.LoadModel(_mlContext, (Utilities.GetModelPath(appFolder, algorithmTypeName, false, "InductedToHallOfFame", true)));

            // Create prediction engine
            var predEngineOnHallOfFameBallot   = _mlContext.Model.CreatePredictionEngine <MLBBaseballBatter, MLBHOFPrediction>(loadedModelOnHallOfFameBallot);
            var predEngineInductedToHallOfFame = _mlContext.Model.CreatePredictionEngine <MLBBaseballBatter, MLBHOFPrediction>(loadedModelInductedToHallOfFame);

            // Create statistics for bad, average & great player
            var badMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Bad Player",
                ID                   = "Bad101",
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 2f,
                AB                   = 100f,
                R                  = 10f,
                H                  = 30f,
                Doubles            = 1f,
                Triples            = 1f,
                HR                 = 1f,
                RBI                = 10f,
                SB                 = 10f,
                BattingAverage     = 0.3f,
                SluggingPct        = 0.15f,
                AllStarAppearances = 1f,
                //MVPs = 0f,
                //TripleCrowns = 0f,
                //GoldGloves = 0f,
                //MajorLeaguePlayerOfTheYearAwards = 0f,
                TB = 200f
            };
            var averageMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Average Player",
                ID                   = "Avg101",
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 2f,
                AB                   = 8393f,
                R                  = 1162f,
                H                  = 2340f,
                Doubles            = 410f,
                Triples            = 8f,
                HR                 = 439f,
                RBI                = 1412f,
                SB                 = 9f,
                BattingAverage     = 0.279f,
                SluggingPct        = 0.486f,
                AllStarAppearances = 6f,
                //MVPs = 0f,
                //TripleCrowns = 0f,
                //GoldGloves = 0f,
                //MajorLeaguePlayerOfTheYearAwards = 0f,
                TB = 4083f
            };
            var greatMLBBatter = new MLBBaseballBatter
            {
                FullPlayerName       = "Great Player",
                ID                   = "Great101",
                InductedToHallOfFame = false,
                LastYearPlayed       = 0f,
                OnHallOfFameBallot   = false,
                YearsPlayed          = 20f,
                AB                   = 10000f,
                R                  = 1900f,
                H                  = 3500f,
                Doubles            = 500f,
                Triples            = 150f,
                HR                 = 600f,
                RBI                = 1800f,
                SB                 = 400f,
                BattingAverage     = 0.350f,
                SluggingPct        = 0.65f,
                AllStarAppearances = 14f,
                //MVPs = 2f,
                //TripleCrowns = 1f,
                //GoldGloves = 4f,
                //MajorLeaguePlayerOfTheYearAwards = 2f,
                TB = 7000f
            };

            var batters = new List <MLBBaseballBatter> {
                badMLBBatter, averageMLBBatter, greatMLBBatter
            };
            // Convert the list to an IDataView
            var newPredictionsData = _mlContext.Data.LoadFromEnumerable(batters);

            // Make the predictions for both OnHallOfFameBallot & InductedToHallOfFame
            var predBadOnHallOfFameBallot       = predEngineOnHallOfFameBallot.Predict(badMLBBatter);
            var predBadInductedToHallOfFame     = predEngineInductedToHallOfFame.Predict(badMLBBatter);
            var predAverageOnHallOfFameBallot   = predEngineOnHallOfFameBallot.Predict(averageMLBBatter);
            var predAverageInductedToHallOfFame = predEngineInductedToHallOfFame.Predict(averageMLBBatter);
            var predGreatOnHallOfFameBallot     = predEngineOnHallOfFameBallot.Predict(greatMLBBatter);
            var predGreatInductedToHallOfFame   = predEngineInductedToHallOfFame.Predict(greatMLBBatter);

            // Report the results
            Console.WriteLine("Algorithm Used for sample Model Prediction: " + algorithmTypeName);
            Console.WriteLine("\n");
            Console.WriteLine("Bad Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predBadOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predBadOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predBadInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predBadInductedToHallOfFame.Probability);
            Console.WriteLine();
            Console.WriteLine("Average Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predAverageOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predAverageOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predAverageInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predAverageInductedToHallOfFame.Probability);
            Console.WriteLine();
            Console.WriteLine("Great Baseball Player Prediction");
            Console.WriteLine("------------------------------");
            Console.WriteLine("On HOF Ballot Prediction: " + predGreatOnHallOfFameBallot.Prediction.ToString() + " | " + "Probability: " + predGreatOnHallOfFameBallot.Probability);
            Console.WriteLine("HOF Inducted Prediction:  " + predGreatInductedToHallOfFame.Prediction.ToString() + " | " + "Probability: " + predGreatInductedToHallOfFame.Probability);

            #endregion

            // End of job, report time
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(string.Format("Finished Baseball Predictions - Training Model Job in: {0} seconds", Math.Round(sw.Elapsed.TotalSeconds, 2)));
            Console.ReadLine();
        }