public static IServiceCollection AddSentimentModelCreationService <TModelLoader>(
            this IServiceCollection services,
            string modelName        = "SentimentModel",
            double testSlipFraction = 0.1,
            Action <ModelLoderFileOptions>?configure = null)
            where TModelLoader : ModelLoader
        {
            var builder = services.AddModelCreationService <SentimentIssue, BinaryClassificationMetricsResult>(modelName);

            builder.AddSourceLoader <SentimentIssue, BinaryClassificationMetricsResult, EmbeddedSourceLoader <SentimentIssue> >(options =>
            {
                options.Sources.Add(new SourceLoaderFile <SentimentIssue>
                {
                    // overrides default loading mechanism
                    CustomAction = () =>
                    {
                        var inputs = EmbeddedResourceHelper
                                     .GetRecords <InputSentimentIssueRow>("Content.wikiDetoxAnnotated40kRows.tsv", delimiter: "\t", hasHeaderRecord: true);

                        // convert int to boolean values
                        var result = new List <SentimentIssue>();
                        foreach (var item in inputs)
                        {
                            var newItem = new SentimentIssue
                            {
                                Label = item.Label != 0,
                                Text  = item.comment
                            };

                            result.Add(newItem);
                        }

                        return(result);
                    }
                });
            });

            builder.AddModelLoader <SentimentIssue, BinaryClassificationMetricsResult, TModelLoader>(configure);

            builder.ConfigureModelDefinition <SentimentIssue, BinaryClassificationMetricsResult>(
                testSlipFraction,
                options =>
            {
                options.TrainingPipelineConfigurator = (mlContext) =>
                {
                    // STEP 2: Common data process configuration with pipeline data transformations
                    var dataProcessPipeline = mlContext.Transforms.Text.FeaturizeText(outputColumnName: "Features", inputColumnName: nameof(SentimentIssue.Text));

                    // STEP 3: Set the training algorithm, then create and config the modelBuilder
                    var trainer          = mlContext.BinaryClassification.Trainers.SdcaLogisticRegression(labelColumnName: "Label", featureColumnName: "Features");
                    var trainingPipeline = dataProcessPipeline.Append(trainer);

                    return(new TrainingPipelineResult(trainingPipeline, trainer.ToString()));
                };

                options.EvaluateConfigurator = (mlContext, model, trainerName, dataView, _) =>
                {
                    // STEP 5: Evaluate the model and show accuracy stats
                    var predictions = model.Transform(dataView);
                    var metrics     = mlContext.BinaryClassification.Evaluate(data: predictions, labelColumnName: "Label", scoreColumnName: "Score");

                    return(new BinaryClassificationMetricsResult(trainerName, metrics));
                };
            });

            builder.ConfigureService <SentimentIssue, BinaryClassificationMetricsResult>(
                options =>
            {
                options.DataLoader = async(loader, cancellationToken) =>
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var data = loader.LoadData();
                    return(await Task.FromResult(data));
                };

                options.TrainModelConfigurator = (modelBuilder, data, logger) =>
                {
                    // 1. load ML data set
                    modelBuilder.LoadData(data);

                    // 1. load default ML data set
                    modelBuilder.BuildDataView();

                    // 2. build training pipeline
                    var buildTrainingPipelineResult = modelBuilder.BuildTrainingPipeline();

                    // 3. train the model
                    var trainModelResult = modelBuilder.TrainModel();

                    // 4. evaluate quality of the pipeline
                    var evaluateResult = modelBuilder.Evaluate();
                    logger.LogInformation(evaluateResult.ToString());

                    return(evaluateResult);
                };

                options.ClassifyTestConfigurator = async(modelBuilder, logger, cancellationToken) =>
                {
                    // 5. predict on sample data
                    var sw = ValueStopwatch.StartNew();

                    var tasks = new List <Task>
                    {
                        ClassifyAsync(modelBuilder, "This is a very rude movie", false, logger, cancellationToken),
                        ClassifyAsync(modelBuilder, "Hate All Of You're Work", true, logger, cancellationToken)
                    };

                    await Task.WhenAll(tasks);
                };
            });

            return(services);
        }