Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| import math | |
| import torch | |
| from featurelens.metrics import ( | |
| js_divergence_from_logits, | |
| reconstruction_metrics, | |
| safe_log_probability, | |
| sequence_logprob_summary, | |
| sparse_topk_cosine, | |
| target_token_logprobs, | |
| ) | |
| def test_reconstruction_metrics_perfect_match() -> None: | |
| x = torch.tensor([1.0, 2.0, 3.0]) | |
| metrics = reconstruction_metrics(x, x.clone()) | |
| assert metrics['mse'] == 0.0 | |
| assert metrics['nmse'] == 0.0 | |
| assert abs(metrics['cosine'] - 1.0) < 1e-6 | |
| def test_js_divergence_zero_for_identical_logits() -> None: | |
| logits = torch.tensor([1.0, 2.0, -1.0]) | |
| assert abs(js_divergence_from_logits(logits, logits)) < 1e-8 | |
| def test_safe_log_probability_is_finite_at_zero() -> None: | |
| assert safe_log_probability(0.0) < 0.0 | |
| def test_target_logprobs_score_every_continuation_token() -> None: | |
| # prompt length = 2, target ids = [1, 0]. | |
| # Target token 0 is predicted from logits row 1; token 1 from row 2. | |
| logits = torch.tensor( | |
| [ | |
| [0.0, 0.0], | |
| [0.0, 2.0], | |
| [3.0, 0.0], | |
| [0.0, 0.0], | |
| ] | |
| ) | |
| values = target_token_logprobs(logits, prompt_length=2, target_ids=[1, 0]) | |
| expected_0 = torch.log_softmax(logits[1], dim=-1)[1] | |
| expected_1 = torch.log_softmax(logits[2], dim=-1)[0] | |
| assert torch.allclose(values, torch.stack([expected_0, expected_1])) | |
| def test_sequence_summary_total_and_mean_are_consistent() -> None: | |
| logits = torch.tensor( | |
| [ | |
| [0.0, 0.0], | |
| [0.0, 2.0], | |
| [3.0, 0.0], | |
| [0.0, 0.0], | |
| ] | |
| ) | |
| total, mean, token_values = sequence_logprob_summary( | |
| logits, | |
| prompt_length=2, | |
| target_ids=[1, 0], | |
| ) | |
| assert len(token_values) == 2 | |
| assert math.isclose(total, sum(token_values), rel_tol=1e-6) | |
| assert math.isclose(mean, total / 2.0, rel_tol=1e-6) | |
| def test_sparse_topk_cosine_is_one_for_identical_sparse_vectors() -> None: | |
| cosine = sparse_topk_cosine([1, 3], [2.0, 1.0], [1, 3], [2.0, 1.0]) | |
| assert abs(cosine - 1.0) < 1e-9 | |
| def test_sparse_topk_cosine_is_zero_for_disjoint_support() -> None: | |
| cosine = sparse_topk_cosine([1, 3], [2.0, 1.0], [2, 4], [5.0, 7.0]) | |
| assert cosine == 0.0 | |
| def test_contrastive_log_odds_reports_preference_shift() -> None: | |
| from featurelens.metrics import contrastive_log_odds | |
| baseline, modified, delta = contrastive_log_odds( | |
| baseline_a=-4.0, | |
| modified_a=-3.5, | |
| baseline_b=-2.0, | |
| modified_b=-2.2, | |
| ) | |
| assert math.isclose(baseline, -2.0) | |
| assert math.isclose(modified, -1.3) | |
| assert math.isclose(delta, 0.7) | |
| def test_decoder_cosine_matrix_and_joint_norm_ratio() -> None: | |
| from featurelens.metrics import decoder_cosine_matrix, joint_direction_norm_ratio | |
| orthogonal = torch.tensor([[1.0, 0.0], [0.0, 2.0]]) | |
| matrix = decoder_cosine_matrix(orthogonal) | |
| assert torch.allclose(matrix, torch.eye(2), atol=1e-6) | |
| joint_norm, independent_norm, ratio = joint_direction_norm_ratio(orthogonal) | |
| assert math.isclose(joint_norm, math.sqrt(5.0), rel_tol=1e-6) | |
| assert math.isclose(independent_norm, math.sqrt(5.0), rel_tol=1e-6) | |
| assert math.isclose(ratio, 1.0, rel_tol=1e-6) | |
| def test_joint_norm_ratio_detects_cancellation() -> None: | |
| from featurelens.metrics import joint_direction_norm_ratio | |
| opposing = torch.tensor([[1.0, 0.0], [-1.0, 0.0]]) | |
| joint_norm, independent_norm, ratio = joint_direction_norm_ratio(opposing) | |
| assert math.isclose(joint_norm, 0.0, abs_tol=1e-8) | |
| assert independent_norm > 0 | |
| assert math.isclose(ratio, 0.0, abs_tol=1e-8) | |