pub struct TasteConfig {
pub n_features: usize,
pub k_styles: usize,
pub n_stars: usize,
pub theta_prior_std: Option<f64>,
pub recency_half_life: Option<f64>,
pub fused: Vec<Vec<usize>>,
pub fused_rho: Option<f64>,
}Expand description
Model configuration.
Fields§
§n_features: usizeFeature dimension (after standardization).
k_styles: usizeNumber of style components (mixture of linear experts).
n_stars: usizeNumber of star categories (ratings 0..n_stars).
theta_prior_std: Option<f64>Prior std of each θ coordinate. None → 1/√n_features.
recency_half_life: Option<f64>Recency half-life in observations: an observation h places back in
the log weighs 0.5^(h / half_life) in the likelihood, so old taste
fades as new evidence arrives. None → no forgetting.
fused: Vec<Vec<usize>>Groups of φ coordinates that measure one perceptual thing, and so share a latent per-style mean instead of being independent draws.
Empty by default, which is the flat prior this model has always had.
The caller supplies indices because only it knows the feature names;
see SessionConfig for the brightness group it fills in.
§Why a fused prior rather than dropping a column
rolloff_mean, zcr_mean and centroid_mean are three genuine
measurements of brightness, and over 1200 prior draws they carry VIFs
of ~16.9 / ~9.7 / ~5.9 — rolloff_mean is the worst-conditioned
coordinate in φ. Dropping any of them discards real signal: they
disagree about which brightness (spectral tilt, high-frequency
energy, and waveform sign changes are not the same statistic), and a
listener who prefers one shading over another is expressing something
the survivors cannot represent alone.
A shared mean says what is actually true: these coordinates are
correlated a priori, so evidence about one is partial evidence about
the others. The model can still separate them when the data insist —
Self::sigma_within is what buys that freedom — but with few duels
it pools them instead of splitting an ill-conditioned ridge three ways
at random, which is the failure mode a high VIF names.
fused_rho: Option<f64>How strongly a fused group’s coordinates are correlated a priori,
in [0, 1). None → 0.0, i.e. off — see Self::fused_rho for
the measurement that switched it off.
Parameterized as a correlation rather than as an inner SD so that the
marginal prior on each coordinate is unchanged: with
σ_μ = σ_θ√ρ and σ_within = σ_θ√(1−ρ), every θ still has prior
variance σ_θ² and only the covariance between group members moves.
At ρ = 0 the program is exactly the flat one.
That distinction is not cosmetic. An earlier version of this used
σ_within = σ_θ/2 with σ_μ = σ_θ, which quietly inflated the
marginal variance to 1.25 σ_θ² — so it changed the prior’s scale
as well as its correlation, and any measurement of “does fusing help”
was really measuring two changes at once.
Implementations§
Source§impl TasteConfig
impl TasteConfig
Sourcepub fn mixture(n_features: usize, k_styles: usize) -> Self
pub fn mixture(n_features: usize, k_styles: usize) -> Self
A K-style mixture config for the given feature dimension.
Sourcepub fn fused_rho(&self) -> f64
pub fn fused_rho(&self) -> f64
Prior correlation within a fused group, clamped to [0, 0.99].
§Why this ships at zero
The machinery is implemented, correct and switched off, the way
RefineKeep::Best and
Acquisition::Thompson are kept after losing. Two gates were run and
they disagreed, which is the finding.
Swept against the always-on closed-loop gate — five seeds, a real posterior fit against a synthetic listener — fusing helps:
rho mean posterior/truth r
0.00 0.657 (the flat prior, reproduced exactly)
0.25 0.702 <- best
0.50 0.644
0.75 fails the per-seed floor (seed 0x2 at 0.437)Measured on the climb at rho = 0.25 — 48 paired seeds, the gate that asks what the pool is actually worth to the listener — it hurts, and not marginally:
paired (fused − flat) 10% trimmed −0.579 ± 0.188 (−3.09 se)
median −0.726
sign 16 better / 32 worse, p = 0.029
climbed 41/48 → 38/48Both are true, and the reason is that they measure different things.
The closed-loop gate scores θ recovery, where pooling an
ill-conditioned ridge is a real regularizer. The climb scores the true
utility of the pool the search delivers, and there the pooling is a
bias: this listener’s taste puts 2.0 on centroid_mean and exactly 0
on the other two, so shrinking them together drags the one coefficient
that matters toward two that do not, and the search aims worse.
That is the general warning, and it is worth more than the feature. A VIF says the three brightness coordinates move together across patches — a fact about φ. Fusing their coefficients asserts that a listener’s preferences about them move together — a fact about people, which does not follow from the first and was not measured.
Re-open this if the listener model ever gains a reason to believe preferences follow φ’s correlation structure; the sweep and both gates are here to re-run.
Sourcepub fn sigma_group(&self) -> f64
pub fn sigma_group(&self) -> f64
SD of a fused group’s latent mean: σ_θ√ρ.
Sourcepub fn sigma_within(&self) -> f64
pub fn sigma_within(&self) -> f64
SD of a fused coordinate about its group mean: σ_θ√(1−ρ). Together
with Self::sigma_group this keeps the marginal at σ_θ.
Sourcepub fn effective_fused(&self) -> &[Vec<usize>]
pub fn effective_fused(&self) -> &[Vec<usize>]
The groups actually in force. Empty when ρ = 0, so ρ = 0 is the
flat prior node for node — no latent means, no extra sites, the same
program this model has always run. A guard rather than an accident: a
zero-SD Normal is not a distribution, and “turn the feature off”
should not depend on remembering to also clear fused.
Sourcepub fn sigma_theta(&self) -> f64
pub fn sigma_theta(&self) -> f64
Prior SD of one θ coordinate, corrected for the max-of-K utility so
that Var(u_a − u_b) is invariant to K (module doc).
Trait Implementations§
Source§impl Clone for TasteConfig
impl Clone for TasteConfig
Source§fn clone(&self) -> TasteConfig
fn clone(&self) -> TasteConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TasteConfig
impl Debug for TasteConfig
Source§impl<'de> Deserialize<'de> for TasteConfig
impl<'de> Deserialize<'de> for TasteConfig
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for TasteConfig
impl PartialEq for TasteConfig
Source§fn eq(&self, other: &TasteConfig) -> bool
fn eq(&self, other: &TasteConfig) -> bool
self and other values to be equal, and is used by ==.