Using value functions¶
A value function turns a coalition’s model output into a payoff \(v(S)\) by
comparing it with the base generation (the output for the full prompt). Value
functions are independent of the estimand (Shapley or Banzhaf) and the estimator
(how coalitions are sampled). explanation.value_fn selects one by name.
|
Payoff of a coalition output |
|---|---|
|
Cosine similarity of TF-IDF vectors of the output and the base generation, with IDF weights frozen on all outputs of the run |
|
Cosine similarity of the output and base embeddings (static or contextual) |
|
|
|
Mean log probability of the output’s own tokens; needs a backend that returns them |
|
|
Coming from legacy MLLM-Shap maps these to the U1–U4 utility labels used by legacy MLLM-Shap.
TF-IDF cosine¶
from mllm_shap import GenerationRecord, TfIdfCosineValue
base = GenerationRecord(
text="hello world",
text_token_rows=((1, 0), (0, 1)),
)
candidate = GenerationRecord(
text="hello there",
text_token_rows=((1, 0), (2, 3)),
)
value_fn = TfIdfCosineValue()
value_fn.fit((base, candidate))
utility = value_fn.score(base, candidate)
print(round(utility, 4))
Output
0.3361
The pipeline calls fit() once on
every output of the run, so all coalitions are scored with the same IDF weights,
and reanalyze() refits on the archived outputs.
Embedding similarity¶
from mllm_shap import CosineEmbeddingValue, GenerationRecord
from mllm_shap.domain.enums import EmbeddingMode
base = GenerationRecord(text="base", embedding=(1.0, 0.0))
candidate = GenerationRecord(text="candidate", embedding=(0.9, 0.1))
value_fn = CosineEmbeddingValue(embedding_mode=EmbeddingMode.STATIC)
similarity = value_fn.score(base, candidate)
print(round(similarity, 4))
Output
0.9939
In a run the mode comes from explanation.embedding_mode (static or
contextual): the registry builds the value function with a
PluginContext and the plugin reads the setting
from it, so the YAML below is all you need:
explanation:
value_fn: embedding_cosine
embedding_mode: contextual
Where the vectors come from depends on the backend. The embedding value functions
declare needs_embeddings (RequiresEmbeddings),
so the pipeline asks the backend for model embeddings
(GenerationConfig.compute_embeddings): the transformers backend returns the
mean of the output tokens’ input embeddings (static) and of the last hidden
state (contextual), and explain scores those. Backends without embeddings
(mock, LM Studio, HTTP API) give deterministic hash vectors of the generated text,
which only make sense for tests. reanalyze() always
scores the hash vectors, because archives store the generated text only, so for a
transformers run its embedding values differ from those of explain.
Settings of third-party value functions go under
explanation.plugin_options.<name>; see Extending mllm-shap.
Logprob utility¶
from mllm_shap import GenerationRecord, LogprobValue
candidate = GenerationRecord(text="answer", logprobs=(-0.1, -0.2, -0.3))
utility = LogprobValue().score(candidate, candidate)
print(round(utility, 4))
Output
-0.2
v(S) is the mean natural-log probability of the tokens the model generated
for coalition S, so it lies in (-inf, 0] and an empty generation scores 0.
It measures how confident the model is in its own answer; the base generation is
not read, and v(N) is the base generation’s own mean.
LogprobValue declares needs_logprobs
(RequiresLogprobs). A run with a backend that is
not SupportsLogprobs (mock, lmstudio)
raises ConfigError before any generation. A record that
still arrives without logprobs (an HTTP server that ignores the request) raises
LogprobsUnavailableError. Archives store logprobs per
coalition, and reanalyze() refuses logprob on
an archive whose rows have none. Backend support: Generative backends.
Word error rate and deletion curves¶
reference_wer scores how correct an answer is: with
plugin_options: {reference_wer: {reference: "i had that curiosity"}} a
transcription prompt earns 1 for the exact transcript, ignoring case and
punctuation. word_error_rate() is the metric alone.
deletion_curve() tests an attribution’s faithfulness. It removes
the k highest-ranked players at once, for each k, and rescores the
coalition left. A lower area means the value
falls sooner. Score the curve with a different value function than the
explanation, or the check is circular. Pass the explained run’s backend to
reuse its model:
curve = await deletion_curve(wer_config, snapshot, output.result.values)
print(curve.removed, curve.values, round(curve.area, 3))
Attribution normalizers¶
Normalizers rescale aggregated attributions for presentation. They do not
change coalition payoffs, so base_value and grand_value stay comparable
across normalizers, and efficiency only holds for identity.
from mllm_shap import MinMaxNormalizer
raw = [1.0, -2.0, 3.0]
displayed = MinMaxNormalizer().normalize(raw)
print(displayed)
Output
[0.37499999999999994, 0.0, 0.625]
Config keys: identity, abs_sum, power_shift, min_max. See
Configuration and Value functions and normalizers.
examples/archive_reanalyze.ipynb (embedded on Examples) rescores
one archived run with every value function and normalizer.