Download tests/test_path_patching_paths.py from gexinh/vlm-probe: direct link, hf CLI and curl.
- Browser
- Download file 5.62 kB
-
https://huggingface.co/spaces/gexinh/vlm-probe/resolve/main/tests/test_path_patching_paths.py
- Command line
-
hf download hf://spaces/gexinh/vlm-probe/tests/test_path_patching_paths.py
-
curl -L -o test_path_patching_paths.py https://huggingface.co/spaces/gexinh/vlm-probe/resolve/main/tests/test_path_patching_paths.py
5.62 kB
| """Candidate-route workflow tested with a real deterministic decoder.""" | |
| import math | |
| from pathlib import Path | |
| import sys | |
| import unittest | |
| import torch | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| from demos.path_routes import aggregate_pair_effects, measure_receiver_paths, select_candidates | |
| from test_path_patching import CircuitModel | |
| from vlm_probing import CapabilityError, Prober | |
| class CharacterTokenizer: | |
| """Exact token boundaries for the real-decoder integration test.""" | |
| chat_template = None | |
| def __call__(self, text, **kwargs): | |
| assert kwargs == {"return_tensors": "pt", "add_special_tokens": False} | |
| ids = torch.tensor([[ord(character) - ord("a") + 1 for character in text]]) | |
| return {"input_ids": ids, "attention_mask": torch.ones_like(ids)} | |
| class ReceiverPathTests(unittest.TestCase): | |
| def setUpClass(cls): | |
| cls.threads = torch.get_num_threads() | |
| torch.set_num_threads(1) | |
| def tearDownClass(cls): | |
| torch.set_num_threads(cls.threads) | |
| def test_candidate_selection_respects_decoder_order_and_physical_head_counts(self): | |
| counts = {layer: 2 if layer % 2 else 3 for layer in range(20)} | |
| ranking = [{"layer": 17, "head": 1, "mean_effect": 0.9}, | |
| {"layer": 19, "head": 1, "mean_effect": -0.8}, | |
| {"layer": 18, "head": 2, "mean_effect": 0.7}, | |
| {"layer": 2, "head": 2, "mean_effect": -0.6}, | |
| {"layer": 3, "head": 0, "mean_effect": 0.6}] | |
| senders, receivers = select_candidates(list(reversed(ranking)), counts, 3, 2) | |
| self.assertEqual(receivers, [(19, 1, "q"), (18, 2, "q")]) | |
| self.assertEqual(senders, [(17, 1), (2, 2), (3, 0)]) | |
| for sender in senders: | |
| self.assertLess(sender[0], min(receiver[0] for receiver in receivers)) | |
| with self.assertRaisesRegex(ValueError, "unavailable"): | |
| select_candidates([*ranking, {"layer": 19, "head": 2, "mean_effect": 5}], counts) | |
| with self.assertRaisesRegex(ValueError, "duplicate"): | |
| select_candidates([*ranking, ranking[0]], counts) | |
| with self.assertRaisesRegex(ValueError, "not enough ranked sender"): | |
| select_candidates(ranking, counts, 4, 2) | |
| def test_aggregation_averages_per_pair_ratios_and_keeps_zero(self): | |
| records = [{"raw_delta": 2, "normalizer": 4, "normalized_effect": 0.5}, | |
| {"raw_delta": 0, "normalizer": 1, "normalized_effect": 0.0}, | |
| {"raw_delta": 1, "normalizer": 0, "normalized_effect": None}] | |
| self.assertEqual(aggregate_pair_effects(records), 0.25) | |
| self.assertNotEqual(aggregate_pair_effects(records), (2 + 0) / (4 + 1)) | |
| self.assertIsNone(aggregate_pair_effects([records[-1]])) | |
| with self.assertRaisesRegex(ValueError, "finite"): | |
| aggregate_pair_effects([{"normalized_effect": math.inf}]) | |
| def test_workflow_records_actual_query_paths_and_zero_controls(self): | |
| with torch.random.fork_rng(devices=[]): | |
| torch.manual_seed(194) | |
| model = CircuitModel() | |
| probe = Prober(model) | |
| pairs = [{"id": identifier, "clean_prompt": clean, "donor_prompt": donor, | |
| "clean_answer": "c", "donor_answer": "k"} | |
| for identifier, clean, donor in (("one", "abcd", "efgh"), | |
| ("two", "dcba", "hgfe"))] | |
| artifact = measure_receiver_paths(probe, pairs, CharacterTokenizer(), "cpu", | |
| [(0, 0), (1, 0)], [(2, 0, "q")]) | |
| self.assertEqual(len(artifact["edges"]), 2) | |
| self.assertEqual(artifact["actual_forward_passes"], 28) | |
| self.assertEqual(len(model.records), 28) | |
| self.assertEqual(len(artifact["controls"]), 4) | |
| self.assertTrue(all(control["passed"] for control in artifact["controls"])) | |
| self.assertTrue(all(abs(control["raw_delta"]) <= 1e-8 for control in artifact["controls"])) | |
| for edge in artifact["edges"]: | |
| self.assertEqual(edge["receiver"], [2, 0, "q"]) | |
| self.assertEqual([pair["pair_id"] for pair in edge["per_pair"]], ["one", "two"]) | |
| for pair in edge["per_pair"]: | |
| self.assertAlmostEqual(pair["raw_delta"], pair["patched_margin"] - pair["clean_margin"]) | |
| self.assertAlmostEqual(pair["normalizer"], pair["donor_margin"] - pair["clean_margin"]) | |
| self.assertAlmostEqual(pair["normalized_effect"], pair["raw_delta"] / pair["normalizer"], places=6) | |
| self.assertEqual(edge["mean_normalized_effect"], | |
| aggregate_pair_effects(edge["per_pair"])) | |
| self.assertFalse(probe.adapter._active) | |
| for module in model.modules(): | |
| self.assertFalse(module._forward_hooks) | |
| self.assertFalse(module._forward_pre_hooks) | |
| def test_invalid_query_capability_or_direction_fails_before_any_forward(self): | |
| model = CircuitModel() | |
| probe = Prober(model) | |
| pair = {"id": "one", "clean_prompt": "abcd", "donor_prompt": "efgh", | |
| "clean_answer": "c", "donor_answer": "k"} | |
| with self.assertRaisesRegex(ValueError, "precede"): | |
| measure_receiver_paths(probe, [pair], CharacterTokenizer(), "cpu", [(2, 0)], [(1, 0, "q")]) | |
| probe.spec.path_qkv.pop(2) | |
| with self.assertRaises(CapabilityError): | |
| measure_receiver_paths(probe, [pair], CharacterTokenizer(), "cpu", [(0, 0)], [(2, 0, "q")]) | |
| self.assertFalse(model.records) | |
| if __name__ == "__main__": | |
| unittest.main() | |