示例#1
0
        /// <summary>
        /// Unfortunately, we were not able to ship the dataset for this chapter,
        /// so the code will not run.
        /// It is still the correct code that was used to produce the result for the chapter.
        ///
        /// Defines the entry point of the application.
        /// The first argument, if present, sets the folder to save the output artifacts in.
        /// </summary>
        public static void Main(string[] args)
        {
            InitializeUI();
            Outputter outputter = Outputter.GetOutputter(Contents.ChapterName);
            // We expect the data in a folder near the repository.
            const string dataPath = @"../../../../../../HarnessingTheCrowdData";

            try
            {
                // Parameters of the experiment to run.
                // Modify if you want to run only some of the models, try different numbers of communities, etc.
                var experimentParameters = new ExperimentParameters
                {
                    ExperimentNamePrefix = string.Empty,
                    RandomSeed           = 12347,
                    FractionGoldLabelsReservedForTraining = 0.3,
                    UseOnlyTweetsWithGoldLabels           = false,
                    UseGoldLabelsInTraining  = false,
                    NumberOfTrainingTweets   = 10000,
                    NumDataSizes             = 10,
                    NumberOfCommunitiesSweep = new[] { 1, 2, 3 },
                    VocabularyThreshold      = 10,
                    ModelTypes = ModelTypes.MajorityVote
                                 | ModelTypes.HonestWorker
                                 | ModelTypes.BiasedWorker
                                 | ModelTypes.BiasedCommunity
                                 | ModelTypes.BiasedCommunityWords
                };

                RunExperiments(outputter, experimentParameters, dataPath);
            }
            catch (Exception e)
            {
                Console.WriteLine($"\nAn unhandled exception was thrown:\n{e}");
            }
            finally
            {
                if (args.Length == 1)
                {
                    Console.WriteLine("\n\nSaving outputs...");
                    outputter.SaveOutputAsProducedFlattening(args[0]);
                    Console.WriteLine("Done saving.");
                }
            }
        }
示例#2
0
        RunSweep(
            CrowdDataWithText[] trainingDataSets,
            CrowdDataWithText validationDataSet,
            ModelBase model,
            CorpusInformation corpusInformation,
            Func <CrowdDataMapping, ModelBase, ModelRunnerBase, ModelRunnerBase> runnerCreator,
            Func <ModelRunnerBase, Dictionary <string, object> > trainingResultGetter,
            Func <ModelRunnerBase, Dictionary <string, object>, Dictionary <string, object> > validationResultGetter,
            ExperimentParameters experimentParameters,
            CrowdDataWithText validationDataSetNoLabels    = null,
            List <ModelRunnerBase> previousTrainingRunners = null,
            int numIterations = 200,
            Dictionary <string, Dictionary <string, object> > resultStorage = null)
        {
            var       allResultsForThisModel     = resultStorage ?? new Dictionary <string, Dictionary <string, object> >();
            var       trainingRunners            = new List <ModelRunnerBase>();
            const int NumIterationsForValidation = 5;

            for (var i = 0; i < trainingDataSets.Length; i++)
            {
                var currentModelName =
                    $"{model.Name}_{i}"; // Chart code assumes suffix of dash then number for ordering chart points
                var trainingData    = trainingDataSets[i];
                var trainingMapping = new CrowdDataWithTextMapping(
                    trainingData,
                    LabelValuesToString,
                    corpusInformation);

                var trainingRunner = runnerCreator.Invoke(trainingMapping, model, previousTrainingRunners?[i]);
                trainingRunners.Add(trainingRunner);
                Rand.Restart(experimentParameters.RandomSeed);
                RunModel(trainingRunner, currentModelName + "_Training", numIterations,
                         experimentParameters.UseGoldLabelsInTraining);
                var trainingResults = trainingResultGetter?.Invoke(trainingRunner) ?? new Dictionary <string, object>();

                trainingResults[ErrorsKey]        = trainingRunner.GetErrors();
                trainingResults[WorkerMetricsKey] = GetWorkerMetrics(trainingRunner.DataMapping.Data, trainingRunner, experimentParameters.MaximumNumberWorkers);

                var currentResults =
                    new Dictionary <string, object> {
                    [TrainingKey] = trainingResults
                };

                var validationMapping = new CrowdDataWithTextMapping(
                    validationDataSet,
                    LabelValuesToString,
                    corpusInformation);

                var validationRunner = runnerCreator.Invoke(validationMapping, model, trainingRunner);
                Rand.Restart(experimentParameters.RandomSeed);
                var validationMetrics = RunModel(validationRunner, currentModelName + "_Validation",
                                                 NumIterationsForValidation, false);

                foreach (var prediction in validationRunner.Posteriors.TrueLabel)
                {
                    Console.WriteLine(prediction);
                }

                var validationResults = validationResultGetter.Invoke(validationRunner, validationMetrics);
                validationResults[ErrorsKey]        = validationRunner.GetErrors();
                validationResults[WorkerMetricsKey] = GetWorkerMetrics(validationRunner.DataMapping.Data,
                                                                       validationRunner, experimentParameters.MaximumNumberWorkers);
                currentResults[ValidationKey] = validationResults;

                if (validationDataSetNoLabels != null)
                {
                    var validationMappingNoLabels = new CrowdDataWithTextMapping(
                        validationDataSetNoLabels,
                        LabelValuesToString,
                        corpusInformation);

                    var validationNoLabelsRunner =
                        runnerCreator.Invoke(validationMappingNoLabels, model, trainingRunner);
                    Rand.Restart(experimentParameters.RandomSeed);
                    var validationMetricsNoLabels = RunModel(validationNoLabelsRunner,
                                                             currentModelName + "_ValidationNoLabels", NumIterationsForValidation, false);
                    currentResults[ValidationNoLabelsKey] =
                        validationResultGetter.Invoke(validationNoLabelsRunner, validationMetricsNoLabels);
                }

                allResultsForThisModel[$"TrainingPercent_{Math.Min((i + 1) * 100 / trainingDataSets.Length, 100)}"] =
                    currentResults;
            }

            return(allResultsForThisModel, trainingRunners);
        }
示例#3
0
        public static void RunExperiments(Outputter outputter, ExperimentParameters experimentParameters, string dataPath)
        {
            var engine = new InferenceEngine(new ExpectationPropagation())
            {
                ShowFactorGraph = false,
                ShowWarnings    = true,
                ShowProgress    = false
            };

            // Set engine flags
            engine.Compiler.WriteSourceFiles    = true;
            engine.Compiler.UseParallelForLoops = false;

            string labelsFileName     = Path.Combine(dataPath, "HarnessingTheCrowdLabels.tsv");
            string goldLabelsFileName = Path.Combine(dataPath, "HarnessingTheCrowdGoldLabels.tsv");
            string textsFileName      = Path.Combine(dataPath, "HarnessingTheCrowdTexts.tsv");

            if (!File.Exists(labelsFileName) ||
                !File.Exists(goldLabelsFileName) ||
                !File.Exists(textsFileName))
            {
                throw new FileNotFoundException("Unfortunately, we were not able to ship the data necessary to run the code for this chapter.");
            }

            var crowdData = CrowdDataWithText.LoadData(
                labelsFileName,
                goldLabelsFileName,
                textsFileName,
                new HashSet <int>(LabelValuesToString.Keys));

            Rand.Restart(experimentParameters.RandomSeed);

            // Preparing the input data
            var split              = crowdData.SplitData(experimentParameters.FractionGoldLabelsReservedForTraining);
            var fullTrainingData   = split[CrowdData.Mode.Training];
            var fullValidationData = split[CrowdData.Mode.Validation];

            var trainingData = (CrowdDataWithText)fullTrainingData.LimitData(
                maxNumTweets: experimentParameters.UseOnlyTweetsWithGoldLabels
                    ? fullTrainingData.NumGoldTweets
                    : experimentParameters.NumberOfTrainingTweets,
                randomSeed: experimentParameters.RandomSeed);

            if (experimentParameters.UseBalancedTrainingSets)
            {
                trainingData = (CrowdDataWithText)fullTrainingData.LimitData(
                    maxNumTweets: experimentParameters.UseOnlyTweetsWithGoldLabels
                        ? fullTrainingData.NumGoldTweets
                        : experimentParameters.NumberOfTrainingTweets,
                    balanceTweetsByLabel: true,
                    randomSeed: experimentParameters.RandomSeed);
            }

            var validationData = (CrowdDataWithText)fullValidationData.LimitData(
                experimentParameters.NumberOfValidationJudgments,
                randomSeed: experimentParameters.RandomSeed);

            var validationDataWithNoWorkerLabels = (CrowdDataWithText)validationData.LimitData(maxNumWorkers: 0);

            var trainingWorkerMetrics   = GetWorkerMetrics(trainingData, null, experimentParameters.MaximumNumberWorkers);
            var validationWorkerMetrics =
                GetWorkerMetrics(validationData, null, experimentParameters.MaximumNumberWorkers);
            var corpus            = trainingData.TweetTexts.Values.Distinct().ToArray();
            var corpusInformation = CorpusInformation.BuildCorpusInformation(
                corpus,
                experimentParameters.VocabularyThreshold);

            var totalNumTrainingJudgments = trainingData.CrowdLabels.Count;
            var numTrainingJudgments      = Util.ArrayInit(
                experimentParameters.NumDataSizes,
                i => Math.Round(totalNumTrainingJudgments * (i + 1) / (double)experimentParameters.NumDataSizes));

            var trainingDataSets = Util.ArrayInit(
                experimentParameters.NumDataSizes,
                i => (CrowdDataWithText)trainingData.LimitData((int)numTrainingJudgments[i],
                                                               randomSeed: experimentParameters.RandomSeed));

            var trainingTweets         = trainingData.Tweets;
            var trainingCrowdWorkers   = trainingData.Workers;
            var validationTweets       = validationData.Tweets;
            var validationCrowdWorkers = validationData.Workers;

            var allDataInfo = new Dictionary <string, object>
            {
                ["Full training data"]           = fullTrainingData,
                ["Full validation data"]         = fullValidationData,
                ["Validation data"]              = validationData,
                ["Training data"]                = trainingData,
                ["Training data sets"]           = trainingDataSets,
                ["Validation data set"]          = validationData,
                ["Training set tweets"]          = trainingTweets,
                ["Training set crowd workers"]   = trainingCrowdWorkers,
                ["Validation set tweets"]        = validationTweets,
                ["Validation set crowd workers"] = validationCrowdWorkers,
                ["CorpusInformation"]            = corpusInformation
            };

            outputter.Out(allDataInfo, "Inputs");

            WriteCrowdDataDetailsToConsole(fullTrainingData, "Full training data");
            WriteCrowdDataDetailsToConsole(fullValidationData, "Full validation data");
            WriteCrowdDataDetailsToConsole(trainingData, "Training set");
            WriteCrowdDataDetailsToConsole(validationData, "Validation set");

            var now    = DateTime.Now;
            var nowStr = $"{now.Year:D4}{now.Month:D2}{now.Day:D2}T{now.Hour:D2}{now.Minute:D2}{now.Second:D2}";

            var experimentInfo = new Dictionary <string, object>
            {
                ["Date"]       = nowStr,
                ["Parameters"] = experimentParameters,
                [$"Training {WorkerMetricsKey}"]         = trainingWorkerMetrics,
                [$"Validation {WorkerMetricsKey}"]       = validationWorkerMetrics,
                [$"Training {LabelCountHistogramKey}"]   = GetWorkerLabelCountHistogram(trainingData),
                [$"Validation {LabelCountHistogramKey}"] = GetWorkerLabelCountHistogram(validationData)
            };

            outputter.Out(experimentInfo, "Experiment");

            // Data structures for comparing results
            var metricsForPlots = new List <Metric> {
                Metric.Accuracy, Metric.AverageLogProb
            };
            var metricsStripCharts = new Dictionary <string, Dictionary <string, PointWithBounds[]> >();
            var metricsBarCharts   = new Dictionary <string, Dictionary <string, double> >();

            foreach (var metric in metricsForPlots)
            {
                var metricString = metric.ToString();
                metricsStripCharts[metricString] = new Dictionary <string, PointWithBounds[]>();
                metricsBarCharts[metricString]   = new Dictionary <string, double>();
            }

            // Creates a snapshot of currently accumulated comparison metrics
            Dictionary <string, object> getCurrentComparisonSnapshot()
            {
                var metricsStripChartsSnapshot =
                    new Dictionary <string, Dictionary <string, PointWithBounds[]> >();

                var metricsBarChartsSnapshot =
                    new Dictionary <string, Dictionary <string, double> >();

                foreach (var metric in metricsForPlots)
                {
                    var metricString = metric.ToString();
                    metricsStripChartsSnapshot[metricString] = new Dictionary <string, PointWithBounds[]>(metricsStripCharts[metricString]);
                    metricsBarChartsSnapshot[metricString]   = new Dictionary <string, double>(metricsBarCharts[metricString]);
                }
                return(new Dictionary <string, object>
                {
                    ["Strip Charts"] = metricsStripChartsSnapshot,
                    ["Bar Charts"] = metricsBarChartsSnapshot
                });
            }

            // Running models as specified in experiment parameters
            bool section2Started = false;
            Dictionary <string, object> section2Comparison = null;

            if ((experimentParameters.ModelTypes & ModelTypes.MajorityVote) != 0)
            {
                Console.WriteLine($"\n{Contents.S2TryingOutTheWorkerModel.NumberedName}\n");
                section2Started = true;
                var accuracyString      = Metric.Accuracy.ToString();
                var majorityVoteString  = "Majority Vote";
                var majorityVoteMetrics = RunMajorityVoteModel(validationData, majorityVoteString);
                outputter.Out(majorityVoteMetrics, Contents.S2TryingOutTheWorkerModel.NumberedName, majorityVoteString);

                metricsStripCharts[accuracyString][majorityVoteString] = GetChartPointsForMetric(
                    Enumerable.Repeat(majorityVoteMetrics, trainingDataSets.Length).ToList(),
                    Metric.Accuracy.ToString(),
                    numTrainingJudgments);

                metricsBarCharts[accuracyString][majorityVoteString] =
                    metricsStripCharts[accuracyString][majorityVoteString].Last().Y;

                section2Comparison = getCurrentComparisonSnapshot();
                outputter.Out(section2Comparison, Contents.S2TryingOutTheWorkerModel.NumberedName, "Comparison");
            }

            if ((experimentParameters.ModelTypes & ModelTypes.HonestWorker) != 0)
            {
                if (!section2Started)
                {
                    Console.WriteLine($"\n{Contents.S2TryingOutTheWorkerModel.NumberedName}\n");
                }
                var currentModel        = new HonestWorkerModel(engine);
                var honestWorkerMetrics = new Dictionary <string, Dictionary <string, object> >();
                outputter.Out(honestWorkerMetrics, Contents.S2TryingOutTheWorkerModel.NumberedName, currentModel.Name);

                honestWorkerMetrics = RunSweep(
                    trainingDataSets,
                    validationData,
                    currentModel,
                    corpusInformation,
                    (map, model, runner) => new HonestWorkerRunner(map, (HonestWorkerModel)model, runner),
                    GetHonestWorkerTrainingResults,
                    (runner, metrics) =>
                    GetHonestWorkerValidationResults(runner, metrics, experimentParameters.MaximumNumberWorkers),
                    experimentParameters,
                    resultStorage: honestWorkerMetrics).Results;


                foreach (var metric in metricsForPlots)
                {
                    metricsStripCharts[metric.ToString()][currentModel.Name] =
                        GetChartPointsForMetric(honestWorkerMetrics, metric, numTrainingJudgments);
                    metricsBarCharts[metric.ToString()][currentModel.Name] =
                        metricsStripCharts[metric.ToString()][currentModel.Name].Last().Y;
                }

                section2Comparison = getCurrentComparisonSnapshot();
                outputter.Out(section2Comparison, Contents.S2TryingOutTheWorkerModel.NumberedName, "Comparison");
            }

            if ((experimentParameters.ModelTypes & ModelTypes.BiasedWorker) != 0)
            {
                Console.WriteLine($"\n{Contents.S3CorrectingForWorkerBiases.NumberedName}\n");
                var currentModel        = new BiasedWorkerModel(engine);
                var biasedWorkerMetrics = new Dictionary <string, Dictionary <string, object> >();
                outputter.Out(biasedWorkerMetrics, Contents.S3CorrectingForWorkerBiases.NumberedName, currentModel.Name);

                biasedWorkerMetrics = RunSweep(
                    trainingDataSets,
                    validationData,
                    currentModel,
                    corpusInformation,
                    (map, model, runner) => new BiasedWorkerModelRunner(map, (BiasedWorkerModel)model, runner),
                    runner => GetBiasedWorkerTrainingResults(runner, experimentParameters.MaximumNumberWorkers),
                    GetValidationResults,
                    experimentParameters,
                    resultStorage: biasedWorkerMetrics).Results;

                foreach (var metric in metricsForPlots)
                {
                    metricsStripCharts[metric.ToString()][currentModel.Name] =
                        GetChartPointsForMetric(biasedWorkerMetrics, metric, numTrainingJudgments);
                    metricsBarCharts[metric.ToString()][currentModel.Name] =
                        metricsStripCharts[metric.ToString()][currentModel.Name].Last().Y;
                }

                outputter.Out(getCurrentComparisonSnapshot(), Contents.S3CorrectingForWorkerBiases.NumberedName, "Comparison");
            }

            if ((experimentParameters.ModelTypes & ModelTypes.BiasedCommunity) != 0)
            {
                Console.WriteLine($"\n{Contents.S4CommunitiesOfWorkers.NumberedName}\n");
                engine.Algorithm = new VariationalMessagePassing();
                foreach (int numCommunities in experimentParameters.NumberOfCommunitiesSweep)
                {
                    var currentModel = new BiasedCommunityModel(engine)
                    {
                        NumberOfCommunities = numCommunities
                    };
                    var result = new Dictionary <string, Dictionary <string, object> >();
                    outputter.Out(result, Contents.S4CommunitiesOfWorkers.NumberedName, currentModel.Name);

                    result = RunSweep(
                        trainingDataSets,
                        validationData,
                        currentModel,
                        corpusInformation,
                        (map, model, runner) => new BiasedCommunityModelRunner(
                            map,
                            (BiasedCommunityModel)model,
                            runner),
                        GetBiasedCommunityTrainingResults,
                        GetValidationResults,
                        experimentParameters,
                        resultStorage: result).Results;

                    foreach (var metric in metricsForPlots)
                    {
                        metricsStripCharts[metric.ToString()][currentModel.Name] =
                            GetChartPointsForMetric(result, metric, numTrainingJudgments);
                        metricsBarCharts[metric.ToString()][currentModel.Name] =
                            metricsStripCharts[metric.ToString()][currentModel.Name].Last().Y;
                    }
                }

                outputter.Out(getCurrentComparisonSnapshot(), Contents.S4CommunitiesOfWorkers.NumberedName, "Comparison");
            }

            if ((experimentParameters.ModelTypes & ModelTypes.BiasedCommunityWords) != 0)
            {
                Console.WriteLine($"\n{Contents.S5MakingUseOfTheTweets.NumberedName}\n");
                // VMP is an order of magnitude faster than EP here, while producing almost the same result
                engine.Algorithm = new VariationalMessagePassing();
                foreach (int numCommunities in experimentParameters.NumberOfCommunitiesSweep)
                {
                    var currentModel = new BiasedCommunityWordsModel(engine)
                    {
                        NumberOfCommunities = numCommunities
                    };
                    var result = new Dictionary <string, Dictionary <string, object> >();
                    outputter.Out(result, Contents.S5MakingUseOfTheTweets.NumberedName, currentModel.Name);

                    result = RunSweep(
                        trainingDataSets,
                        validationData,
                        currentModel,
                        corpusInformation,
                        (map, model, runner) => new BiasedCommunityWordsRunner(
                            (CrowdDataWithTextMapping)map,
                            (BiasedCommunityWordsModel)model,
                            runner),
                        GetBiasedCommunityWordsRunnerResults,
                        GetValidationResults,
                        experimentParameters,
                        validationDataWithNoWorkerLabels,
                        null,
                        50,
                        resultStorage: result).Results;

                    foreach (var metric in metricsForPlots)
                    {
                        metricsStripCharts[metric.ToString()][currentModel.Name] =
                            GetChartPointsForMetric(result, metric, numTrainingJudgments);
                        metricsBarCharts[metric.ToString()][currentModel.Name] =
                            metricsStripCharts[metric.ToString()][currentModel.Name].Last().Y;
                    }
                }

                outputter.Out(getCurrentComparisonSnapshot(), Contents.S5MakingUseOfTheTweets.NumberedName, "Comparison");
            }

            Console.WriteLine("\nCompleted all experiments.");
        }