---
base_model: microsoft/phi-4
library_name: peft
pipeline_tag: text-generation
license: mit
tags:
- base_model:adapter:microsoft/phi-4
- lora
- transformers
- interpretability
- nla
- activation-reconstructor
---
# NLA Activation Reconstructor — Phi-4 (14B), universal multi-layer
The validator half of the NLA pair. Given a natural-language description and the
target depth, it reconstructs the residual-stream activation vector that the
description refers to. High reconstruction cosine is the proof that the
[Activation Verbalizer](https://huggingface.co/anicka/nla-phi4-universal-av-v2)'s descriptions
carry real geometric information rather than plausible-sounding narration.
Part of the [nla-at-home](https://github.com/anicka-net/nla-at-home) project — a
DIY replication of Anthropic's
[Natural Language Autoencoders](https://www.anthropic.com/research/natural-language-autoencoders).
## What it does
Read a description (e.g. *"To select all records from the users table where the
surname is 'Smith', use SELECT * FROM users WHERE surname = 'Smith'; the query
structure selects this SQL syntax."*) plus the depth it came from, and predict
the activation vector. The package is a LoRA adapter on Phi-4 plus seven
per-layer linear value heads that map the adapted model's hidden state to the
d_model=5120 target space (one head per trained layer).
## Training
- **Base model**: microsoft/phi-4 (14B, d_model 5120)
- **Objective**: centered direction-MSE + **InfoNCE** contrastive
(temp 20.0, weight 1.0, 56 extra negatives from the target bank) — the v3
objective, verified to beat plain dir-MSE (+0.013 retrieval, matched 9 layers).
- **Layers**: 4, 10, 16, 19, 25, 32, 38 (depths ~10/25/40/47/63/80/96%) · **Epochs**: 5 (patience 2) · **Batch**: 8
- **Learning rate**: 1e-4 · **LoRA**: r16, alpha32, dropout0.05, targets qkv_proj/o_proj/gate_up_proj/down_proj
- **Training data**: corpus v2 — ~6000 safe-category texts, descriptions
grounded in Phi-4's own greedy replies at deep layers. See the
[dataset](https://huggingface.co/datasets/anicka/nla-at-home-corpus).
- **Best mean cosine** (gold description → reconstruction, held-out): 0.678
## Evaluation
| Metric | This adapter | Notes |
|---|---|---|
| Roundtrip cos (AV→AR) | 0.587 | full pair on 50-text double-holdout; 95% CI [0.55, 0.62] (B=20k) |
| GT-description ceiling | 0.676 | AR fed gold descriptions on the same holdout |
Reference point: Anthropic's `kitft/nla-qwen2.5-7b-L20-ar` roundtrip 0.769 (a 7B
single-layer AR). This is a 14B universal multi-layer AR, a harder setting.
Honest decomposition on the holdout: with **gold** descriptions the AR
reconstructs to 0.676 cosine (the ceiling for this pair); the full round-trip
lands at 0.587. The 0.089 gap is the Activation Verbalizer's verbalization loss,
not the AR's — the bottleneck is decoding the activation into words, not
reconstructing the activation from words. Per-layer ceilings climb with depth
(L4 0.553 → L19 0.731), consistent with deeper layers carrying more
description-legible content.
## Usage
The package is a LoRA adapter plus a separate `value_heads.safetensors`
(seven `[5120, 5120]` heads keyed by layer index). Reconstruction loads the
adapter, runs the base model with the AR prompt, takes the last-token hidden
state at `layer+1`, and applies the matching value head:
```python
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
from safetensors.torch import load_file
from huggingface_hub import hf_hub_download
REPO = "anicka/nla-phi4-universal-ar-v2"
AR_PROMPT = "Summary of the following text: {e} "
tok = AutoTokenizer.from_pretrained(REPO)
tok.padding_side = "left"
if tok.pad_token is None:
tok.pad_token = tok.eos_token
base = AutoModelForCausalLM.from_pretrained("microsoft/phi-4", torch_dtype=torch.bfloat16)
model = PeftModel.from_pretrained(base, REPO).to("cuda").eval()
vh = load_file(hf_hub_download(REPO, "value_heads.safetensors"))
heads = {int(k): v.float().to("cuda") for k, v in vh.items()}
@torch.no_grad()
def reconstruct(description, layer):
enc = tok(AR_PROMPT.format(e=description), return_tensors="pt",
truncation=True, max_length=256).to("cuda")
h = model(**enc, output_hidden_states=True).hidden_states[layer + 1][:, -1, :].float()
return h @ heads[layer].T # [1, 5120] reconstructed activation
```
See `scripts/eval_roundtrip_phi4.py` (phase `ar`) in
[nla-at-home](https://github.com/anicka-net/nla-at-home) for the batched
round-trip pipeline and the centered-cosine metric.
## Related
- [anicka/nla-phi4-universal-av-v2](https://huggingface.co/anicka/nla-phi4-universal-av-v2) — companion Activation Verbalizer
- [nla-at-home-corpus](https://huggingface.co/datasets/anicka/nla-at-home-corpus) — training data (corpus v2)
- [nla-at-home](https://github.com/anicka-net/nla-at-home) — full pipeline code
- [Anthropic NLA](https://www.anthropic.com/research/natural-language-autoencoders)