interp-engine 0.0.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,155 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ from typing import Any, Callable, Coroutine, Sequence
6
+
7
+ import numpy as np
8
+ from neuron_explainer.activations.activations import ActivationRecord
9
+ from neuron_explainer.explanations.calibrated_simulator import (
10
+ CalibratedNeuronSimulator,
11
+ LinearCalibratedNeuronSimulator,
12
+ )
13
+ from neuron_explainer.explanations.explanations import (
14
+ ScoredSequenceSimulation,
15
+ ScoredSimulation,
16
+ SequenceSimulation,
17
+ )
18
+ from neuron_explainer.explanations.simulator import ExplanationNeuronSimulator, NeuronSimulator
19
+
20
+
21
+ def flatten_list(list_of_lists: Sequence[Sequence[Any]]) -> list[Any]:
22
+ return [item for sublist in list_of_lists for item in sublist]
23
+
24
+
25
+ def correlation_score(
26
+ real_activations: Sequence[float] | np.ndarray,
27
+ predicted_activations: Sequence[float] | np.ndarray,
28
+ ) -> float:
29
+ return np.corrcoef(real_activations, predicted_activations)[0, 1]
30
+
31
+
32
+ def score_from_simulation(
33
+ real_activations: ActivationRecord,
34
+ simulation: SequenceSimulation,
35
+ score_function: Callable[[Sequence[float] | np.ndarray, Sequence[float] | np.ndarray], float],
36
+ ) -> float:
37
+ return score_function(real_activations.activations, simulation.expected_activations)
38
+
39
+
40
+ def rsquared_score_from_sequences(
41
+ real_activations: Sequence[float] | np.ndarray,
42
+ predicted_activations: Sequence[float] | np.ndarray,
43
+ ) -> float:
44
+ return float(
45
+ 1
46
+ - np.mean(np.square(np.array(real_activations) - np.array(predicted_activations)))
47
+ / np.mean(np.square(np.array(real_activations)))
48
+ )
49
+
50
+
51
+ def absolute_dev_explained_score_from_sequences(
52
+ real_activations: Sequence[float] | np.ndarray,
53
+ predicted_activations: Sequence[float] | np.ndarray,
54
+ ) -> float:
55
+ return float(
56
+ 1
57
+ - np.mean(np.abs(np.array(real_activations) - np.array(predicted_activations)))
58
+ / np.mean(np.abs(np.array(real_activations)))
59
+ )
60
+
61
+
62
+ async def make_explanation_simulator(
63
+ explanation: str,
64
+ calibration_activation_records: Sequence[ActivationRecord],
65
+ model_name: str,
66
+ calibrated_simulator_class: type[CalibratedNeuronSimulator] = LinearCalibratedNeuronSimulator,
67
+ ) -> CalibratedNeuronSimulator:
68
+ """
69
+ Make a simulator that uses an explanation to predict activations and calibrates it on the given
70
+ activation records.
71
+ """
72
+ simulator = ExplanationNeuronSimulator(model_name, explanation)
73
+ calibrated_simulator = calibrated_simulator_class(simulator)
74
+ await calibrated_simulator.calibrate(calibration_activation_records)
75
+ return calibrated_simulator
76
+
77
+
78
+ async def _simulate_and_score_sequence(
79
+ simulator: NeuronSimulator, activations: ActivationRecord
80
+ ) -> ScoredSequenceSimulation:
81
+ """Score an explanation of a neuron by how well it predicts activations on a sentence."""
82
+ simulation = await simulator.simulate(activations.tokens)
83
+ logging.debug(simulation)
84
+ rsquared_score = score_from_simulation(activations, simulation, rsquared_score_from_sequences)
85
+ absolute_dev_explained_score = score_from_simulation(
86
+ activations, simulation, absolute_dev_explained_score_from_sequences
87
+ )
88
+ scored_sequence_simulation = ScoredSequenceSimulation(
89
+ simulation=simulation,
90
+ true_activations=activations.activations,
91
+ ev_correlation_score=score_from_simulation(activations, simulation, correlation_score),
92
+ rsquared_score=rsquared_score,
93
+ absolute_dev_explained_score=absolute_dev_explained_score,
94
+ )
95
+ return scored_sequence_simulation
96
+
97
+
98
+ def aggregate_scored_sequence_simulations(
99
+ scored_sequence_simulations: list[ScoredSequenceSimulation],
100
+ ) -> ScoredSimulation:
101
+ """
102
+ Aggregate a list of scored sequence simulations. The logic for doing this is non-trivial for EV
103
+ scores, since we want to calculate the correlation over all activations from all sequences at
104
+ once rather than simply averaging per-sequence correlations.
105
+ """
106
+ all_true_activations: list[float] = []
107
+ all_expected_values: list[float] = []
108
+ for scored_sequence_simulation in scored_sequence_simulations:
109
+ all_true_activations.extend(scored_sequence_simulation.true_activations or [])
110
+ all_expected_values.extend(scored_sequence_simulation.simulation.expected_activations)
111
+ ev_correlation_score = (
112
+ correlation_score(all_true_activations, all_expected_values)
113
+ if len(all_true_activations) > 0
114
+ else None
115
+ )
116
+ rsquared_score = rsquared_score_from_sequences(all_true_activations, all_expected_values)
117
+ absolute_dev_explained_score = absolute_dev_explained_score_from_sequences(
118
+ all_true_activations, all_expected_values
119
+ )
120
+
121
+ return ScoredSimulation(
122
+ scored_sequence_simulations=scored_sequence_simulations,
123
+ ev_correlation_score=ev_correlation_score,
124
+ rsquared_score=rsquared_score,
125
+ absolute_dev_explained_score=absolute_dev_explained_score,
126
+ )
127
+
128
+
129
+ async def simulate_and_score(
130
+ simulator: NeuronSimulator,
131
+ activation_records: Sequence[ActivationRecord],
132
+ ) -> ScoredSimulation:
133
+ """
134
+ Score an explanation of a neuron by how well it predicts activations on the given text
135
+ sequences.
136
+ """
137
+ scored_sequence_simulations = await asyncio.gather(
138
+ *[
139
+ _simulate_and_score_sequence(
140
+ simulator,
141
+ activation_record,
142
+ )
143
+ for activation_record in activation_records
144
+ ]
145
+ )
146
+ return aggregate_scored_sequence_simulations(scored_sequence_simulations)
147
+
148
+
149
+ async def make_simulator_and_score(
150
+ make_simulator: Coroutine[None, None, NeuronSimulator],
151
+ activation_records: Sequence[ActivationRecord],
152
+ ) -> ScoredSimulation:
153
+ """Chain together creating the simulator and using it to score activation records."""
154
+ simulator = await make_simulator
155
+ return await simulate_and_score(simulator, activation_records)