locked
Logistic factor problems RRS feed

  • Question

  • Hello Infer.net team,

    I'm having problems with a variant of the Chess Analysis model I've made where the outcome probabilities are instead determined by a Logistic function of the performances. The model compiles but under EP the model give an error message on the Logistic factor message and whilst the model runs under VMP the numbers it converges to aren't very good. My code is below; what am I doing wrong here in specifying this model? N.B. I set the SerialSchedules parameter to false to avoid the bug in 2.6 which would stop the model compiling otherwise.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine(new VariationalMessagePassing());
                engine.Compiler.UseSerialSchedules = false;
                engine.Compiler.UseParallelForLoops = true;
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(5, 2 * 2);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 2 * 1 * 1);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 2 * 0.1 * 0.1);
    
                var performancePrecision = Variable.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                            }
                        }
                    }
                }
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                if (!inferParameters)
                {
                    // fix the true parameters
                    performancePrecision.ObservedValue = parameters.performancePrecision;
                    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                }
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game].Named("w");
                        var b = blackPlayer[year][game].Named("b");
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill[year][w], performancePrecision).Named("white_performance");
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill[year][b], performancePrecision).Named("black_performance");
                        Variable<bool> white_delta = Variable.Bernoulli(Variable.Logistic(white_performance - black_performance)).Named("white_delta");
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainFalse(white_delta);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // white wins
                            Variable.ConstrainTrue(white_delta);
                        }
                    }
                }
                //year.AddAttribute(new Sequential());   // helps inference converge faster
    
                engine.NumberOfIterations = 50;
                var skillPost = engine.Infer<Gaussian[][]>(skill);
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", engine.Infer<Gamma>(performancePrecision).GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                }
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 10)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double performancePrecision, skillChangePrecision;
                public double[][] skill;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double performance_diff = Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        bool white_delta = Bernoulli.Sample(MMath.Logistic(performance_diff));
                        int outcome;
                        if(white_delta == true)
                        {
                            outcome = 1;  // white wins
                        }
                        else
                        {
                            outcome = 0;  // black wins
                        }
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }


    Monday, December 29, 2014 4:11 PM

Answers

  • It appears to be a bug in the implementation of that factor.
    • Marked as answer by MarkG87 Thursday, February 5, 2015 2:25 PM
    Thursday, February 5, 2015 12:30 PM
    Owner

All replies

  • This question has now been partially answered here (https://social.microsoft.com/Forums/en-US/a6dfd58e-ee59-4c63-8d67-6bb7195c9527/vmp-algorithm-regularisation-initialisation?forum=infer.net) for why VMP gives bad convergence, so the only remaining question I have here is why this model gives errors on the message passing operation for the Logistic factor using EP.
    • Edited by MarkG87 Monday, January 12, 2015 10:26 PM
    Monday, January 12, 2015 7:58 PM
  • Hello Infer.net team,

    Why does the above model gives errors on the message passing operation for the Logistic factor using EP?

    Tuesday, February 3, 2015 3:34 PM
  • It appears to be a bug in the implementation of that factor.
    • Marked as answer by MarkG87 Thursday, February 5, 2015 2:25 PM
    Thursday, February 5, 2015 12:30 PM
    Owner