Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Utility as a max of experts

A candidate is as good as its best lens thinks it is. Two more obvious designs cannot represent a cross-island comparison at all.

The form

u(x)=maxk1..K  θkz(x),z(x)=φ(x)μsu(x) = \max_{k \in 1..K} ; \theta_k^\top z(x), \qquad z(x) = \frac{\varphi(x) - \mu}{s}

KK style lenses, each a linear functional on the standardized feature vector. Utility is the maximum, not a weighted mixture.

At K=1K = 1 this reduces exactly to Bayesian linear regression on zz, which is a useful property: the mixture is a strict generalization with no special-casing at the boundary.

pub fn utility_mix(&self, phi: &[f64]) -> f64 {
    self.theta.iter()
        .map(|t| dot(t, phi))
        .fold(f64::NEG_INFINITY, f64::max)
}

Why a maximum

Taste is multi-modal. One person can love dark drones and bright plucks ("ambient-me" and "acid-me"), and those are not points on one axis. A single linear utility would average them into a preference for neither, and would then be confidently wrong about both.

The max form gives each island its own lens, and every judgement, including a duel across two islands, compares candidates on the shared scale u=maxkuku = \max_k u_k. A dark drone and a bright pluck are both scored, each by whichever lens likes it most, and the comparison is well-formed.

Drag either lens. Each candidate is coloured by the lens that scores it highest and lit by how highly, the same encoding the instrument's taste map uses. Pull the lenses apart and two islands appear, each with its own idea of what "good" points at. Then press compare K = 1: one direction has to explain both islands at once, and the only direction that does lies between them, describing a taste nobody has.

Two rejected designs, and why

A per-session style latent zsz_s

"One mood per session — sample which lens is active, then use it."

Fails because it cannot represent several islands inside a session. A user who auditions a pad, then a bass, then a pad in one sitting is not switching moods; they have two preferences at once. Whenever the session's latent is wrong for the current candidate, every observation in that session is scored by the wrong lens.

A per-observation marginalized lens

"Marginalize over which lens judges each observation."

Fails on a sharper point: it forces both duel items through the same lens, so a cross-island comparison is unrepresentable. There is no lens under which "the drone beats the pluck" is a sensible statement if the drone lives in lens 1 and the pluck in lens 2, and a duel between them is exactly the question the acquisition rule will ask.

This is not a theoretical objection. A synthetic bimodal user exposed it: the marginalized mixture failed to beat K=1K = 1. Adding capacity made the model no better, which is the signature of capacity the likelihood cannot use.

What max-utility buys structurally

There are no discrete latent sites at all. No lens assignment to sample, no categorical variables, no label-switching during inference to fight. Every site in the model is an f64, which means fugue's generic adaptive single-site MH applies unchanged — no custom kernel, no Rao-Blackwellization.

Label permutation is resolved post hoc instead, by TastePosterior::aligned.

KK is an upper bound, not a claim

K=5K = 5 by default (SessionConfig::k_styles), and the fitted number of live lenses grows with evidence.

Nothing enforces that; it falls out. A lens with no evidence to explain stays near its prior, and style_share reports what fraction of the pool each lens actually claims as its best. A lens claiming ≈0% is idle: the user's taste has fewer islands than KK, and the app dims it rather than inventing a name for it.

So KK is capacity, and the data decides how much gets used.

The prior, and the correction KK forces

θk,jN(0,σθ2),σθ=1d  sK\theta_{k,j} \sim \mathcal{N}(0, \sigma_\theta^2), \qquad \sigma_\theta = \frac{1}{\sqrt{d}; s_K}

The 1/d1/\sqrt{d} factor is standard: with z2d\lVert z \rVert^2 \approx d for a standardized vector, it makes the prior utility of a candidate roughly unit-variance, so likelihood scales stay sane at any feature count.

The sKs_K factor is the correction the max form forces, and it is easy to miss.

Under the prior each uku_k is marginally N(0,1)\mathcal{N}(0,1), so u=maxkuku = \max_k u_k is the maximum of KK iid standard normals — whose standard deviation falls with KK:

KK12345
sKs_K1.0000.8260.7480.7010.669

The mean shift cancels in duels (both sides shift equally) and is absorbed by τ\tau and the cutpoints elsewhere. The variance shrinkage does not cancel. Left uncorrected, Var(uaub)\mathrm{Var}(u_a - u_b) drops from 2.0 at K=1K=1 to 0.90 at K=5K=5 — so growing KK mid-session would quietly make the model less able to express a strong preference.

That is the opposite of what adding capacity should do, and it would present as "the model gets vaguer the longer I use it".

Dividing by sKs_K restores invariance: Var(uaub)\mathrm{Var}(u_a - u_b) is the same at every KK.

What the interface reads off this

QuantityIs
utility_mix(z)(mean,sd)(\text{mean}, \text{sd}) of uu over posterior draws — the glow and size on the taste map
utility(z, k)Lens kk's opinion specifically
best_style(z)Which lens claims this candidate — the hue on the map
responsibilities(z)Posterior probability that each lens is the best one for this candidate
style_share(pool)Per-lens share of the pool, averaged over candidates
prob_prefers(a, b)Eθ[σ(u(a)u(b))]\E_\theta,[\sigma(u(a) - u(b))] — the bank row's percentage

responsibilities is a posterior distribution over which lens applies, which is strictly more informative than an argmax and is what lets a candidate sit visibly between two islands.