Personalised human models#

Personalised human models: start from the population, adapt to each person.

Fitting one model to everyone’s answers assumes that people are alike; fitting each person separately needs thousands of answers (see example 11). Partial pooling sits in between: the population’s spread of parameters becomes a prior, and each person’s parameters are the maximum a posteriori (MAP) estimate

\[\hat x_i = \arg\min_x\; -\log L_i(x) + \tfrac12 (x - \mu)^T \Sigma^{-1} (x - \mu),\]

in the unconstrained parameter space of the model (see quantum_mind.core.Param.to_free()). With few answers the estimate stays near the population; with many it follows the person. A robot can therefore personalise its human model from the first few interactions without over-fitting.

  • PopulationPrior: mean and covariance of the free parameters, estimated from per-person fits (empirical Bayes) or set by hand.

  • fit_map(): MAP fit of one model class to one person’s data.

  • PersonalisedHumanModel: keeps each person’s answer counts and their personalised model, either as a MAP point estimate or, with method='online', as a full posterior updated after every answer (OnlinePersonModel started from the population prior), so the robot also knows how sure it is about this person.

Examples

>>> import numpy as np
>>> from quantum_mind.families.order_effects import BayesOrderModel
>>> from quantum_mind.applications.personalisation import PopulationPrior, fit_map
>>> prior = PopulationPrior(BayesOrderModel, mean=[0.0, 0.0, 0.0], cov=np.eye(3) * 0.5)
>>> few = {'AB': np.array([3, 0, 0, 1]), 'BA': np.array([2, 1, 0, 1])}
>>> r = fit_map(BayesOrderModel, few, prior)
>>> 0.5 < r.model.pA < 0.9                     # pulled toward the population mean 0.5
True

Classes

PopulationPrior

Gaussian prior on a model's free (unconstrained) parameters.

PersonalisedHumanModel

Per-person human models with partial pooling.

Functions

fit_map

Maximum a posteriori fit of one person's data under a population prior.

population_prior

Convenience: fit every person by maximum likelihood and build the empirical-Bayes prior.

class PopulationPrior(model_cls, mean, cov)[source]#

Bases: object

Gaussian prior on a model’s free (unconstrained) parameters.

Parameters:
  • model_cls (type) – Model class whose free_params() the prior describes.

  • mean (array_like) – Prior mean in the free space.

  • cov (array_like) – Prior covariance (positive definite).

classmethod from_fits(fits, shrink=0.01)[source]#

Empirical-Bayes prior from per-person fits.

Parameters:
  • fits (sequence of FitResult) – Maximum-likelihood fits of the same model class to different people (for example fit_individuals(...).results.values()).

  • shrink (float, optional) – Added to the covariance diagonal so it stays invertible with few people.

Returns:

PopulationPrior

online(n_particles=500, design=None, rng=None, **options)[source]#

A per-person posterior that starts from this prior.

Parameters:
  • n_particles (int, optional) – Number of parameter samples.

  • design (iterable, optional) – Design passed to predict.

  • rng (numpy.random.Generator, optional) – Random number generator.

  • **options – Structural options of the model.

Returns:

OnlinePersonModel

neg_log_prior(x)[source]#

Negative log prior density, up to a constant.

Parameters:

x (array_like) – Free parameter vector.

Returns:

float

class PersonalisedHumanModel(model_cls, prior, design=None, outcomes=4, method='map', n_particles=300)[source]#

Bases: object

Per-person human models with partial pooling.

Parameters:
  • model_cls (type) – Model class (for example QuantumOrderModel4D).

  • prior (PopulationPrior) – Population prior; build it with PopulationPrior.from_fits() from earlier people.

  • design (iterable, optional) – Conditions.

  • outcomes (int, optional) – Number of outcome cells per condition (4 for two yes/no questions).

  • method ({'map', 'online'}, optional) – 'map' refits a MAP estimate after every answer; 'online' keeps a particle posterior per person (faster per answer, and gives credible_interval()).

  • n_particles (int, optional) – Particles per person for method='online'.

Examples

>>> svc = PersonalisedHumanModel(BayesOrderModel, prior)
>>> svc.add('alice', 'AB', [1, 0])
>>> svc.predict('alice')['AB']
add(person, condition, answers)[source]#

Record one answer pair and refit that person.

Parameters:
  • person (hashable) – Person identifier.

  • condition (hashable) – Condition (for example the question order 'AB').

  • answers (sequence of int or int) – The answers (two yes/no answers for order designs) or an outcome index.

Returns:

Model – The person’s updated model.

model(person)[source]#

The person’s model (the population mean model for a new person).

Parameters:

person (hashable)

Returns:

Model

predict(person, design=None)[source]#

Predicted answer distribution for one person.

Parameters:
  • person (hashable)

  • design (iterable, optional)

Returns:

dict

credible_interval(person, name, level=0.9)[source]#

Posterior interval of one of a person’s parameters (method='online' only).

Parameters:
  • person (hashable)

  • name (str) – Parameter name.

  • level (float, optional)

Returns:

tuple of float

Raises:

ValueError – If the model keeps MAP estimates, or the person has not answered yet.

fit_map(model_cls, data, prior, design=None, restarts=4, rng=None, **options)[source]#

Maximum a posteriori fit of one person’s data under a population prior.

Parameters:
  • model_cls (type) – Model class (multinomial loss).

  • data (dict) – The person’s answer counts.

  • prior (PopulationPrior) – Prior on the free parameters.

  • design (iterable, optional) – Conditions passed to predict.

  • restarts (int, optional) – Random restarts (the first start is the prior mean).

  • rng (numpy.random.Generator, optional)

  • **options – Structural options of the model.

Returns:

FitResult – loss is the negative log posterior; loglik the log-likelihood at the MAP estimate.

population_prior(model_cls, data_by_person, design=None, restarts=4, rng=None)[source]#

Convenience: fit every person by maximum likelihood and build the empirical-Bayes prior.

Parameters:
Returns:

PopulationPrior