SPB Git

spb/wpai0001_uqo

Public
4.9 KB · 132 lines python
1"""Gate C sanity: hooks are a true no-op when disabled/empty, and interventions have effects."""2import torch34from wp_ai_0001.config import OpSeqTaskConfig, ToyModelConfig5from wp_ai_0001.datasets import opseq6from wp_ai_0001.datasets.common import encode_split7from wp_ai_0001.models.hooks import Add, Capture, GaussianNoise, ProjectSubspace, Replace, Zero8from wp_ai_0001.models.toy_transformer import ToyTransformer91011def _setup(seed=0):12    torch.manual_seed(seed)13    cfg = OpSeqTaskConfig()14    split = opseq.generate(cfg, 64, seed=11, name="sanity")15    vocab = opseq.vocab_for(cfg)16    enc = encode_split(split, vocab, 24)17    model = ToyTransformer(ToyModelConfig(d_model=32, n_layers=2, n_heads=2, d_ff=64, max_seq_len=24), len(vocab)).eval()18    return model, torch.from_numpy(enc["ids"]), torch.from_numpy(enc["prompt_len"] - 1), torch.from_numpy(enc["answers"][:, 0])192021def test_no_hooks_is_bitwise_identity():22    model, ids, pos, _ = _setup()23    with torch.no_grad():24        ref = model(ids, pos)25        assert model.hooks.calls == 026        out = model(ids, pos)27    assert torch.equal(ref, out)282930def test_capture_and_disabled_hooks_are_bitwise_identity():31    model, ids, pos, _ = _setup()32    with torch.no_grad():33        ref = model(ids, pos)34        cap = Capture()35        for n in model.hook_names():36            model.hooks.add(n, cap)37        out_cap = model(ids, pos)38        assert set(cap.store) == set(model.hook_names())39        # a modifying hook that is disabled must also be a no-op40        model.hooks.add("resid_post.0", Zero())41        with model.hooks.disabled():42            out_dis = model(ids, pos)43        model.hooks.enabled = False44        out_dis2 = model(ids, pos)45    assert torch.equal(ref, out_cap) and torch.equal(ref, out_dis) and torch.equal(ref, out_dis2)464748def test_identity_object_returned_when_no_hooks():49    model, ids, pos, _ = _setup()50    x = torch.randn(2, 3, 32)51    assert model.hooks("resid_post.0", x) is x52    model.hooks.enabled = False53    model.hooks.add("resid_post.0", Zero())54    assert model.hooks("resid_post.0", x) is x555657def test_interventions_change_outputs_reproducibly():58    model, ids, pos, _ = _setup()59    with torch.no_grad():60        ref = model(ids, pos)61        model.hooks.add("resid_post.0", Zero(positions=pos))62        a = model(ids, pos)63        b = model(ids, pos)64    assert not torch.equal(ref, a) and torch.equal(a, b)65    # unpatched positions are untouched up to the hooked layer: check resid_post.0 itself66    model.hooks.clear()67    cap = Capture()68    model.hooks.add("resid_post.0", cap)69    with torch.no_grad():70        model(ids, pos)71    clean = cap.store["resid_post.0"]72    model.hooks.clear()73    cap2 = Capture()74    model.hooks.add("resid_post.0", Zero(positions=pos))75    model.hooks.add("resid_post.0", cap2)76    with torch.no_grad():77        model(ids, pos)78    z = cap2.store["resid_post.0"]79    B = ids.shape[0]80    assert torch.all(z[torch.arange(B), pos] == 0)81    mask = torch.ones_like(clean, dtype=torch.bool)82    mask[torch.arange(B), pos] = False83    assert torch.equal(z[mask], clean[mask])848586def test_replace_with_own_activation_is_identity():87    model, ids, pos, _ = _setup()88    cap = Capture()89    model.hooks.add("resid_mid.1", cap)90    with torch.no_grad():91        ref = model(ids, pos)92    model.hooks.clear()93    model.hooks.add("resid_mid.1", Replace(cap.store["resid_mid.1"]))94    with torch.no_grad():95        out = model(ids, pos)96    assert torch.allclose(ref, out, atol=0, rtol=0)979899def test_add_noise_project_have_expected_algebra():100    model, ids, pos, _ = _setup()101    x = torch.randn(4, 5, 32)102    v = torch.randn(32)103    y = Add(v, scale=2.0)(x, model.hooks.__class__ and __import__("wp_ai_0001.models.hooks", fromlist=["HookContext"]).HookContext("h"))104    assert torch.allclose(y - x, 2.0 * v.expand_as(x))105    ctx = __import__("wp_ai_0001.models.hooks", fromlist=["HookContext"]).HookContext("h")106    n1, n2 = GaussianNoise(0.5, seed=3)(x, ctx), GaussianNoise(0.5, seed=3)(x, ctx)107    assert torch.equal(n1, n2) and not torch.equal(n1, x)108    basis = torch.randn(32, 4)109    abl = ProjectSubspace(basis, "ablate")(x, ctx)110    keep = ProjectSubspace(basis, "keep")(x, ctx)111    assert torch.allclose(abl + keep, x, atol=1e-5)112    assert torch.allclose(abl @ basis, torch.zeros(4, 5, 4), atol=1e-4)113114115def test_head_patch_touches_only_selected_head():116    model, ids, pos, _ = _setup()117    cap = Capture()118    model.hooks.add("attn_heads.0", cap)119    with torch.no_grad():120        model(ids, pos)121    src = torch.zeros_like(cap.store["attn_heads.0"])122    model.hooks.clear()123    cap2 = Capture()124    model.hooks.add("attn_heads.0", Replace(src, positions=[2], heads=[1]))125    model.hooks.add("attn_heads.0", cap2)126    with torch.no_grad():127        model(ids, pos)128    out = cap2.store["attn_heads.0"]129    ref = cap.store["attn_heads.0"]130    assert torch.all(out[:, 2, 1] == 0)131    assert torch.equal(out[:, 2, 0], ref[:, 2, 0]) and torch.equal(out[:, :2], ref[:, :2]) and torch.equal(out[:, 3:], ref[:, 3:])132
Files · main
Menu

Theme