webppl icon indicating copy to clipboard operation
webppl copied to clipboard

Usage help: observed data

Open fralik opened this issue 3 years ago • 2 comments

Hi! I would like to model a process very similar to what is described Probabilistic Models of Cognition under the name of Tug of War.

There is a game involving two players. The outcome is always either a win or loss. Each player has some internal strength. Probability of winning player1 over player2 is given as P_ij = strength_i - strength_j.

I have historic data of outcomes for several players and would like to find out each player strength.

If I use PyMC, then the modelling goes like this:

model = pm.Model()
with model:
    strength = pm.Normal("strength", 0, 1, shape=num_players)
    diffs = strength[games.Player1] - strength[games.Player2]
    obs = pm.Bernoulli("wins", logit_p=diffs, observed=games.Player1Wins)
    trace = pm.sample()

where games is a table with 3 columns. Player1 - id of the first player. Player2 - id of the second player. Player1Wins - 1 if player one wins, 0 otherwise.

I tried to come up with that in WebPPL:

var model = function() {
  var strength = mem(function (person) {return gaussian(0, 1)})
  var winner = function (player1, player2) {
    strength(player1) > strength(player2) ? player1 : player2 }
  var beat = function(player1,player2){winner(player1,player2) == player1}

  mapData({data: [
    ['Player-2', 'Player-1'],
    ['Player-3', 'Player-1'],
    ['Player-3', 'Player-2'],
    ['Player-2', 'Player-1'],
    ['Player-3', 'Player-1'],
    ['Player-2', 'Player-1'],
    ['Player-4', 'Player-3'],
    ['Player-4', 'Player-3'],
  ]}, function (gameOutcome) {
    condition(beat(gameOutcome0], gameOutcome[1]))
  })

  return strength('Player-1')
}

var dist = Infer({method: 'MCMC', kernel: 'MH', samples: 100},
                 model)

print('Expected strength: ' + expectation(dist[0]))
viz(dist)

I have a couple of questions regarding this code:

  1. Does it seems right to provide observable data the way I did?
  2. I am unsure how to obtain strength of all players in one go. Names and number of players will be dynamic, I do not know it beforehand.

fralik avatar Aug 16 '22 06:08 fralik

I think I found a way of providing the data that is more similar to PyMC:

var model = function() {
  var strength = mem(function (person) {return gaussian(0, 1)})
  var p_win = function(p1, p2) {
    strength(p1) - strength(p2)
  }
  var sigmoid = function(x) {
    return 1 / (1 + Math.exp(-1 * x))
  }
  
  mapData({data: [
    ['Player-1', 'Player-2', 0],
    ['Player-1', 'Player-3', 0],
    ['Player-2', 'Player-3', 0],
    ['Player-1', 'Player-2', 0],
    ['Player-3', 'Player-1', 1],
    ['Player-2', 'Player-1', 1],
    ['Player-4', 'Player-3', 1],
    ['Player-4', 'Player-3', 1],
  ]}, function (gameOutcome) {
    var logit = p_win(gameOutcome[0], gameOutcome[1])
    var firstWon = gameOutcome[2] == 1 ? true : false
    factor(Bernoulli({p: sigmoid(logit)}).score(firstWon))
  })

  return strength('Player-1')
}

var dist = Infer({method: 'MCMC', kernel: 'MH', samples: 8000}, model)

Still it is not clear to me how to obtain/work with strengths of all players.

fralik avatar Aug 17 '22 09:08 fralik

it may be helpful to use observe instead of factor plus score (though it does something very similar).

you can return the object of all strengths ({player1: strength('Player-1'), player2: ...}) and then eg use viz.marginals` to examine them. some of the examples in the learning as inference section of probmods should help.

ngoodman avatar Aug 18 '22 00:08 ngoodman