A Kalman-filter generalization of rescorla_wagner()'s error-driven
update rule (Dayan & Kakade, 2001; Kruschke, 2008; Gershman, 2015): word-
object associations are tracked as Gaussian beliefs, and each trial's
update is scaled by a Kalman gain, sigma2 / (sigma2 + sigma2_obs). This
makes the effective learning rate adaptive rather than fixed – large (fast
learning) while an association is still uncertain, and automatically
shrinking as confidence accumulates – and it emerges from three
interpretable parameters rather than being hand-set. Between observations,
every association's variance grows by tau2 (the model assumes true
associations drift slowly over time), which keeps the model able to revise
a belief rather than converging to a fixed point.
Arguments
- tau2
Process (diffusion) noise: how much uncertainty about every association grows per trial, whether or not it was observed
- sigma2_obs
Observation noise: how uninformative a single co-occurrence observation is (larger = slower learning per trial)
- sigma2_0
Initial (prior) uncertainty about every association, before any training
- covariance
"independent"(the default): one filter per word-object pair."full": one filter per object, over all words, with a full covariance.- outcomes
"present"(the default) updates only the objects present on a trial;"all"also updates absent objects toward 0.- phi
Optional softmax inverse temperature for the choice rule.
NULL(the default) uses Luce choice.
Details
By default (covariance = "independent"), every word-object association
is its own one-dimensional filter that observes 1 whenever the pair
co-occurs. Associations then don't interact: there is no competition
between a trial's words, and an association is a saturating function of
how often and how recently its pair co-occurred. With a small gain it is
simply co-occurrence counting.
Options
covariance = "full"is the Kalman filter of the associative-learning literature (Dayan & Kakade, 2001; Kruschke, 2008). Each object (outcome) has a weight vector over words with a full covariance matrix. The present words jointly predict the object (the sum of their weights), so they share one prediction error, and the gain is a vector over words. Words that appear together come to have negatively correlated weights, so later evidence about one revises the other: explaining away and backward blocking (retrospective revaluation), as when learning that one word on an earlier trial named an object implies the trial's other words did not. Memory and time grow with the square of the vocabulary.outcomes = "all"treats every object as an outcome on every trial, observing 1 if it is present and 0 if absent, as in the canonical filter, rather than updating only the objects present. Associations can then become negative, which Luce choice treats as 0;phigives a choice rule defined for them.phireplaces Luce choice with a softmax choice rule with inverse temperaturephi(seesoftmax_weights()).
Note that the three noise parameters share a scale redundancy:
multiplying tau2, sigma2_obs and sigma2_0 together by any constant
leaves every Kalman gain – and hence the learned matrix – unchanged. Only
two of the three are identifiable; when fitting, hold one fixed
(conventionally sigma2_obs).
References
Dayan, P., & Kakade, S. (2001). Explaining away in weight space. In Advances in Neural Information Processing Systems 13 (pp. 451-457).
Gershman, S. J. (2015). A unifying probabilistic view of associative learning. PLoS Computational Biology, 11(11), e1004567.
Kruschke, J. K. (2008). Bayesian approaches to associative learning: From passive to active learning. Learning & Behavior, 36(3), 210-226.
Examples
mod <- kalman_filter(tau2 = .01, sigma2_obs = .5, sigma2_0 = 1)
xsl_run(mod, get_example_ambiguous_condition())
#> $fits
#> $fits[[1]]
#> $fits[[1]]$sims
#> NULL
#>
#> $fits[[1]]$responses
#> [,1] [,2] [,3] [,4]
#> [1,] 0.5 0.5 0.5 0.5
#>
#> $fits[[1]]$perf
#> 1 2 3 4
#> 0.5 0.5 0.5 0.5
#>
#> $fits[[1]]$matrix
#> 1 2 3 4
#> 1 0.6688742 0.6688742 0.0000000 0.0000000
#> 2 0.6688742 0.6688742 0.0000000 0.0000000
#> 3 0.0000000 0.0000000 0.6710526 0.6710526
#> 4 0.0000000 0.0000000 0.6710526 0.6710526
#>
#> $fits[[1]]$sse
#> [1] 0
#>
#> $fits[[1]]$data
#> xslData object with label "example condition" and condition "ambiguous"
#> training trials: 2
#> test trials: 0
#> words: 4
#> objects: 4
#> accuracies: 4
#>
#>
#>
#> $sse
#> [1] 0
#>
#> $unweighted_sse
#> [1] 0
#>
# the full-covariance filter, with absent objects as outcomes
mod <- kalman_filter(tau2 = .001, sigma2_obs = 1, sigma2_0 = .5,
covariance = "full", outcomes = "all")
xsl_run(mod, get_example_ambiguous_condition())
#> $fits
#> $fits[[1]]
#> $fits[[1]]$sims
#> NULL
#>
#> $fits[[1]]$responses
#> [,1] [,2] [,3] [,4]
#> [1,] 0.5 0.5 0.5 0.5
#>
#> $fits[[1]]$perf
#> 1 2 3 4
#> 0.5 0.5 0.5 0.5
#>
#> $fits[[1]]$matrix
#> 1 2 3 4
#> 1 0.2502498 0.2502498 0.000000 0.000000
#> 2 0.2502498 0.2502498 0.000000 0.000000
#> 3 0.0000000 0.0000000 0.250499 0.250499
#> 4 0.0000000 0.0000000 0.250499 0.250499
#>
#> $fits[[1]]$sse
#> [1] 0
#>
#> $fits[[1]]$data
#> xslData object with label "example condition" and condition "ambiguous"
#> training trials: 2
#> test trials: 0
#> words: 4
#> objects: 4
#> accuracies: 4
#>
#>
#>
#> $sse
#> [1] 0
#>
#> $unweighted_sse
#> [1] 0
#>