FeatureLens / tests /test_sae.py
ArchitSharma's picture
Initial FeatureLens v0.1.0
9d24374
Raw
History Blame Contribute Delete
1.56 kB
from __future__ import annotations
import torch
from featurelens.sae import SAEWeights
def make_sae() -> SAEWeights:
w_enc_t = torch.tensor(
[
[1.0, 0.0, -1.0, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.5, -1.0],
[0.0, 0.0, 1.0, 0.0, 1.0],
]
)
w_dec = torch.tensor(
[
[1.0, 0.0, 0.0, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 1.0, 0.0, 1.0],
]
)
return SAEWeights(
layer=0,
w_enc_t=w_enc_t,
w_dec=w_dec,
b_enc=torch.zeros(5),
b_dec=torch.zeros(3),
top_k=2,
)
def test_encode_keeps_topk_relu_features() -> None:
sae = make_sae()
encoding = sae.encode(torch.tensor([2.0, 1.0, -1.0]))
assert encoding.indices.shape == (2,)
assert set(encoding.indices.tolist()) == {0, 3}
assert encoding.active_count == 2
assert encoding.activation_for(0) == 2.0
assert encoding.activation_for(4) == 0.0
def test_decode_sparse_uses_selected_decoder_columns() -> None:
sae = make_sae()
encoding = sae.encode(torch.tensor([2.0, 1.0, -1.0]))
reconstructed = sae.decode_sparse(encoding)
assert reconstructed.shape == (3,)
assert torch.isfinite(reconstructed).all()
def test_batched_encode_and_decode() -> None:
sae = make_sae()
hidden = torch.tensor([[2.0, 1.0, -1.0], [0.0, 2.0, 2.0]])
encoding = sae.encode(hidden)
decoded = sae.decode_sparse(encoding)
assert encoding.indices.shape == (2, 2)
assert decoded.shape == hidden.shape