Most of XSLmodels is built around controlled
cross-situational learning experiments: a handful of words and objects,
a designed training order, and a vector of human accuracies to fit with
sum-of-squared-error. This vignette is about the other kind of input –
naturalistic corpora of caregiver speech paired with
the objects present in the scene. There is no human referent-selection
data to fit; instead we ask how well a model’s learned word–object
matrix recovers a gold-standard lexicon.
Two such corpora ship with the package, imported from the wurwur package:
rollins_corpus$data
#> xslData object with label "Rollins" and condition "CHILDES naturalistic corpus (Frank, Goodman & Tenenbaum, 2009)"
#> training trials: 619
#> test trials: 0
#> words: 416
#> objects: 22
#> accuracies: 0
fm_corpus$data
#> xslData object with label "FM" and condition "Frank, Tenenbaum & Fernald naturalistic corpus"
#> training trials: 4763
#> test trials: 0
#> words: 1122
#> objects: 30
#> accuracies: 0rollins_corpus – 619 mother-to-infant
utterances from the Rollins corpus in CHILDES; the corpus the
intentional Bayesian model of Frank, Goodman & Tenenbaum (2009) was
originally fit to.fm_corpus – the larger Frank,
Tenenbaum & Fernald corpus: 4763 utterances, and (unlike Rollins)
the speaker’s referential intention is hand-coded for each
utterance.Each is a list, not a bare xslData:
names(rollins_corpus)
#> [1] "data" "gold" "reference"
names(fm_corpus)
#> [1] "data" "intents" "gold" "gold_variants"
#> [5] "reference"$data is the training xslData;
$gold is the reference lexicon. Note that words and objects
here are the actual strings, not integer ids:
rollins_corpus$data$train$words[[1]]
#> [1] "ahhah" "look" "we" "can" "read" "books" "david"
rollins_corpus$data$train$objects[[1]]
#> [1] "book" "bird" "rattle" "face"
str(rollins_corpus$gold)
#> List of 2
#> $ words : chr [1:34] "baby" "bear" "bigbird" "bigbirds" ...
#> $ objects: chr [1:34] "baby" "bear" "bird" "bird" ...xsl_run() works exactly as it does for experimental
conditions – it returns a fitted object whose matrix is the
learned word (rows) by object (columns) association matrix.
res <- suppressWarnings(xsl_run(baseline(), rollins_corpus$data))
m <- res$fits[[1]]$matrix
dim(m)
#> [1] 416 22
# co-occurrence counts for one word (this corpus is a picture-book session,
# so the objects are animals and toys -- "moocow", "piggie", etc.)
sort(m["moocow", ], decreasing = TRUE)[1:4]
#> cow pig rattle book
#> 8 3 3 1Two things to know:
res$sse and
res$fits[[1]]$perf. Those compare against
data$accuracy, which is empty for a corpus.
xsl_run() still computes them (falling back to
get_perf()), which is where the
"longer object length is not a multiple of shorter" warning
above comes from – the matrix is not square. It is harmless here; we
score against the gold lexicon instead.fgt2009() (below) does joint inference
over the whole corpus and has no learning trajectory, so
control$reps does nothing for it.The evaluation helpers take a
gold_lexicon = list(words, objects). Because a model’s
matrix is real-valued, the score depends on a threshold
for counting an edge as “learned”. [get_fscore()] scores one
threshold:
get_fscore(m / rowSums(m), threshold = 0.1, gold_lexicon = rollins_corpus$gold)
#> # A tibble: 1 × 5
#> threshold precision recall fscore specificity
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 0.1 0.143 0.912 0.247 0.977[get_roc()] sweeps thresholds (row-normalizing the matrix first), and [get_roc_max()] returns the best F-score over that sweep – the standard single-number summary:
roc <- get_roc(m, gold_lexicon = rollins_corpus$gold)
head(roc, 3)
#> # A tibble: 3 × 5
#> threshold precision recall fscore specificity
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 0 0.0294 1 0.0571 0
#> 2 0.01 0.116 1 0.207 0.966
#> 3 0.02 0.118 1 0.212 0.967
get_roc_max(m, gold_lexicon = rollins_corpus$gold)
#> [1] 0.3768116
plot_roc(m, gold_lexicon = rollins_corpus$gold)Comparing a few associative models on Rollins:
assoc_models <- list(
baseline = baseline(),
decay = decay(C = 0.99),
rescorla_wagner = rescorla_wagner(C = 1, alpha = 0.1, beta = 0.1, lambda = 1),
fazly = fazly(lambda = 1e-5, beta = 8500)
)
sapply(assoc_models, function(mod) {
mm <- suppressWarnings(xsl_run(mod, rollins_corpus$data)$fits[[1]]$matrix)
round(get_roc_max(mm, gold_lexicon = rollins_corpus$gold), 3)
})
#> baseline decay rescorla_wagner fazly
#> 0.377 0.343 0.378 0.450fgt2009() is the model built for exactly this problem:
it does joint posterior inference over a word–object lexicon for the
whole corpus at once, marginalizing the speaker’s referential intention
on each utterance. It is a batch MCMC model, so a
single run takes seconds to a minute or two on these corpora (much
slower than the associative tallies) and xslFit$matrix
holds posterior edge marginals
P(word names object | corpus).
The gamma parameter matters here.
gamma is the probability a word is used
referentially. In a controlled experiment every word is a
label, so the default is gamma = 1. Naturalistic speech is
full of non-referential words (“the”, “look”, “you”, “see”), so a corpus
wants the published gamma = 0.1, kappa = 0.05
– otherwise the model is forced to explain every function word as naming
something and learns almost nothing.
res <- xsl_run(
fgt2009(alpha = 3, gamma = 0.1, kappa = 0.05),
rollins_corpus$data
)
m_fgt <- res$fits[[1]]$matrix
# the learned lexicon: edges with posterior probability > 0.5
edges <- which(m_fgt > 0.5, arr.ind = TRUE)
data.frame(word = rownames(m_fgt)[edges[, 1]],
object = colnames(m_fgt)[edges[, 2]],
prob = round(m_fgt[edges], 2))
#> word object prob
#> 1 bear bear 1.00 # correct
#> 2 bottle bear 1.00 # "baby bottle" co-occurs with the bear
#> 3 bigbird bird 1.00 # correct
#> 4 book book 1.00 # correct
#> 5 moocow cow 1.00 # correct
#> 6 hat hat 1.00 # correct
#> 7 oink pig 1.00 # correct
#> ... # 28 edges total
get_roc_max(m_fgt, gold_lexicon = rollins_corpus$gold)
#> [1] 0.454 # MCMC: expect a little run-to-run variationfgt2009()’s alpha penalizes lexicon size
(P(L) proportional to exp(-alpha * |L|)) and
has to scale with corpus size: too small gives a diffuse over-large
lexicon, too large an empty one. The F-vs-alpha curve is
single-peaked, so the right way to “fit” the model to a corpus is a
coarse sweep – which is what [fgt2009_sweep_alpha()] does. Pass
gold and it reports the best-threshold F-score at each
alpha:
sweep <- fgt2009_sweep_alpha(
rollins_corpus$data,
alphas = c(2, 4, 6, 9, 14),
gold = rollins_corpus$gold,
gamma = 0.1, kappa = 0.05,
n_warmup = 50, n_samples = 120 # a lighter sampler for the sweep
)
sweepThe curve rises to a shallow peak around
alpha = 2–4 and then falls off as the prior
forces the lexicon empty. Take the alpha near the peak;
that is the value to report, and the one to use for trial-by-trial
predictions ([predict_referent()]) or further analysis. On this small
corpus fgt2009 is in the same range as the better
associative models (fazly above); its advantage is largest
on data with more varied, informative co-occurrence.
fm_corpus is used the same way, but it is bigger and
comes with extra structure. fm_corpus$intents[[t]] is the
object(s) the speaker actually referred to on utterance t –
empty for the roughly half of utterances that are non-referential:
fm_corpus$data$train$words[[1]]
#> [1] "and" "whats" "that" "is" "this" "a" "puppy" "dog"
fm_corpus$intents[[1]]
#> [1] "dog"
mean(lengths(fm_corpus$intents) == 0) # fraction non-referential
#> [1] 0.5007348It also ships three gold lexicons – a hand-curated one plus two auto-derived from the coded intentions at different co-occurrence thresholds:
lengths(lapply(list(curated = fm_corpus$gold,
strict = fm_corpus$gold_variants$strict,
permissive = fm_corpus$gold_variants$permissive),
`[[`, "words"))
#> curated strict permissive
#> 41 39 116m_fm <- suppressWarnings(xsl_run(baseline(), fm_corpus$data)$fits[[1]]$matrix)
sapply(list(curated = fm_corpus$gold,
strict = fm_corpus$gold_variants$strict,
permissive = fm_corpus$gold_variants$permissive),
function(g) round(get_roc_max(m_fm, gold_lexicon = g), 3))
#> Warning in max(fscores[!is.na(fscores)]): no non-missing arguments to max;
#> returning -Inf
#> curated strict permissive
#> 0.268 0.191 -InfThe permissive lexicon is larger and easier to hit some of, so absolute F-scores are not comparable across the three – pick one and use it consistently when comparing models.
tests/bakeoff_comparison/corpus_gold_comparison.R runs
every model once on each corpus and scores the learned matrix against
the corpus gold lexicon. It is deliberately not a
fitted bake-off: the associative and sampling models use
xsl_model_registry()’s starting parameters (calibrated
defaults, not re-fit here), and fgt2009() uses
gamma = 0.1, kappa = 0.05 with
alpha from the two-point heuristic
round(7 * sqrt(n / 600)).
| model | Rollins | FM |
|---|---|---|
| bayesian_decay | 0.449 | 0.450 |
| fazly | 0.450 | 0.407 |
| fgt2009_rsa | 0.395 | 0.421 |
| fgt2009 | 0.385 | 0.421 |
| uncfam_sampling | 0.394 | 0.360 |
| multi_sampling | 0.394 | 0.352 |
| uncfam | 0.377 | 0.360 |
| uncfam_gamma | 0.377 | 0.360 |
| uncfam_elimination | 0.377 | 0.360 |
| uncfam_attention | 0.377 | 0.351 |
| propose_but_verify | 0.409 | 0.318 |
| softmax_rl | 0.376 | 0.336 |
| pursuit | 0.400 | 0.307 |
| guess_and_test | 0.396 | 0.304 |
| baseline | 0.377 | 0.268 |
| minerva2 | 0.359 | 0.267 |
| rescorla_wagner | 0.312 | 0.290 |
| decay | 0.320 | 0.272 |
| kalman_filter | 0.303 | 0.211 |
| uncfam_predictive | 0.235 | 0.185 |
| todam | 0.174 | 0.048 |
| tilles | 0.080 | 0.048 |
rem() is left out: at roughly 11 minutes per simulation
on FM, averaging the 50 simulations used for the other stochastic models
isn’t practical.
Reading the table:
bayesian_decay and fazly come out
on top (F around 0.45 on both corpora), with
fgt2009 close behind.fgt2009 trades recall for precision.
On FM its precision is ~0.71 against ~0.15–0.34 for the others: the
geometric size prior makes it commit to a small, mostly-correct lexicon
rather than a large noisy one. Whether that is the right tradeoff
depends on what you want the lexicon for.fgt2009 ==
fgt2009_rsa): at alpha = 20 the learned
lexicon is sparse enough that no word names two co-present objects, so
the pragmatic speaker reduces to the literal one.uncfam variants, the two sampling versions (which are meant
to approximate uncfam()), and the hypothesis-testing models
(propose_but_verify, pursuit,
guess_and_test), which do relatively better on Rollins than
on FM.xslControl()’s keep_traj = FALSE default), an
error on the many non-referential utterances with no objects present,
and latent bugs: softmax_rl() and
guess_and_test() compared a sampled guess to object
positions vs labels; guess_and_test()
mishandled a word repeated within one utterance; tilles()
coerced its labels with as.integer() and returned an
unnamed matrix; and propose_but_verify() and
pursuit() drew hypotheses from the wrong set on utterances
with a single object present (common in both corpora).tilles and todam still learn diffuse
lexicons at their default parameters: their best threshold is 0, i.e. no
threshold separates correct pairings from the rest. For
tilles, fitting its parameters lifts F to ~0.40–0.43
(below), so read the low default score as “this parameterisation doesn’t
transfer”, not “the model is broken”.tests/bakeoff_comparison/corpus_gold_fits.R goes a step
further: for each model it runs a (parallel) DEoptim search over the
model’s parameters, maximizing the same best-threshold F against the
gold lexicon (these corpora have no accuracy vector, so
xsl_fit()’s SSE objective does not apply).
fgt2009() is fit by an alpha sweep instead.
uncfam_sampling(), multi_sampling(),
pursuit(), and the episodic-memory models are left at
defaults – averaging many simulations per DEoptim evaluation over 4763
utterances is impractical.
| model | Rollins_default | Rollins_fitted | FM_default | FM_fitted |
|---|---|---|---|---|
| bayesian_decay | 0.449 | 0.465 | 0.450 | 0.560 |
| fgt2009 | NA | 0.452 | NA | 0.529 |
| fgt2009_rsa | NA | 0.447 | NA | 0.515 |
| softmax_rl | 0.360 | 0.447 | 0.299 | 0.429 |
| fazly | 0.450 | 0.463 | 0.407 | 0.412 |
| uncfam_gamma | 0.377 | 0.450 | 0.360 | 0.425 |
| uncfam_attention | 0.377 | 0.420 | 0.351 | 0.417 |
| tilles | 0.080 | 0.427 | 0.048 | 0.400 |
| uncfam | 0.377 | 0.420 | 0.360 | 0.388 |
| guess_and_test | 0.372 | 0.440 | 0.292 | 0.368 |
| uncfam_elimination | 0.377 | 0.404 | 0.360 | 0.400 |
| propose_but_verify | 0.400 | 0.438 | 0.325 | 0.360 |
| rescorla_wagner | 0.312 | 0.389 | 0.290 | 0.343 |
| decay | 0.320 | 0.382 | 0.272 | 0.288 |
| kalman_filter | 0.303 | 0.382 | 0.211 | 0.275 |
| uncfam_predictive | 0.235 | 0.333 | 0.185 | 0.233 |
tilles (+0.35 on both corpora), softmax_rl
(+0.09 / +0.13), uncfam_predictive (+0.10 / +0.05).bayesian_decay fit to FM reaches F =
0.56 – clearly the strongest result – with fgt2009
next at 0.53. fazly barely moves (its defaults were already
near-optimal for these corpora).uncfam_gamma() is the best uncfam
variant once fit (0.45 / 0.43, vs 0.42 / 0.39 for
uncfam()), with a fitted familiarity exponent of 1.56 on
Rollins and 0.77 on FM – so neither corpus is consistent with
familiarity entering linearly, but they disagree on the direction.
uncfam_elimination() gains less (0.40 / 0.40).fgt2009_rsa fit (best of an alpha sweep)
lands just below fgt2009 (0.45 / 0.52 vs 0.45 / 0.53) – the
pragmatic layer neither helps nor hurts once alpha is
chosen, because the fitted lexicon is still too sparse for a word to
name two co-present objects.corpus_gold_fits.rds.
kalman_filter’s sigma2_obs lands high (1511 /
1923, against an upper bound of 2000), but that is a plateau, not an
estimate: the three Kalman parameters share a scale redundancy and the F
landscape is flat in sigma2_obs above a few hundred. Read
its fitted sigma2_obs as “large”.Frank, M. C., Goodman, N. D., & Tenenbaum, J. B. (2009). Using speakers’ referential intentions to model early cross-situational word learning. Psychological Science, 20(5), 578–585.
Frank, M. C., Tenenbaum, J. B., & Fernald, A. (2013). Social and discourse contributions to the determination of reference in cross-situational word learning. Language Learning and Development, 9(1), 1–24.