Instructions to use cds-jb/qwen3-8b-parallel-cot with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use cds-jb/qwen3-8b-parallel-cot with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-8B") model = PeftModel.from_pretrained(base_model, "cds-jb/qwen3-8b-parallel-cot") - Notebooks
- Google Colab
- Kaggle
File size: 9,024 Bytes
74fd26a ca791bd 74fd26a ca791bd 74fd26a ca791bd 74fd26a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | """Single-cell causal patch for the single-token organism.
Overwrite ONE cell value c_i(t) inside the soft token z_t (force cell i = digit d), then CONTINUE
free-running so the change propagates through the learned CA rule, and check the final answer equals
what the *true* CA produces for the altered row. This is a surgical test (one cell, one step) that the
latent at (t,i) causally carries c_i(t) AND that the downstream computation uses it.
Patch (per the card): generate free-running to step t; read z_t = softmax(head(h_t)) in R^{K x 10};
set z_t[i] <- one-hot(d) (other cells untouched); re-embed via the codebook (fed = sum_k z_t[k].C[k])
as the input to z_{t+1}; continue free-running; read the answer from the head at z_T, cell q.
Expected = CA-evolve(c_t with cell i := d) for the remaining T-1-t steps, read at cell q.
python -m latent_threads.eval_single_cellpatch --ckpt .../sg1/diffuse/best --n 400
"""
from __future__ import annotations
import argparse
import json
import os
import random
import dotenv
dotenv.load_dotenv()
_USER = os.environ.get("USER", "jbauer")
os.environ.setdefault("HF_HOME", f"/workspace-vast/{_USER}/hf")
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn.functional as F
from tqdm.auto import tqdm
from latent_threads import tasks as LT
from latent_threads.single import build_single_batch, build_single_mask
from latent_threads.eval_single_report import load, RES
@torch.no_grad()
def patched_answer(model, head, codebook, q_emb, tok, task, batch, im, device, t_p, i_p, d_p):
"""Free-run; at step t_p[j] overwrite cell i_p[j] of the soft token with one-hot(d_p[j]); continue.
Returns the head's predicted digit for each example's queried cell at z_T."""
K, m = task.K, task.m
input_ids, roles, zs_, ls_, lens_ = build_single_batch(tok, task, batch, im, device, with_answer=False)
B, Lmax = input_ids.shape
attn = build_single_mask(roles, zs_, m)
pos = torch.arange(Lmax, device=device)[None].expand(B, Lmax)
E = model.get_input_embeddings()(input_ids)
bidx = torch.arange(B, device=device); zs = torch.tensor(zs_, device=device)
E = E.clone(); E[bidx, zs] = q_emb
out = None
for t in range(m):
out = model(inputs_embeds=E, attention_mask=attn, position_ids=pos, output_hidden_states=True)
if t == m - 1:
break
z = torch.softmax(head(out.hidden_states[-1][bidx, zs + t]).view(B, K, 10).float(), -1) # [B,K,10]
for j in range(B): # patch examples whose t_p == t
if t_p[j] == t:
z[j, i_p[j]] = F.one_hot(torch.tensor(d_p[j], device=device), 10).float()
fed = torch.einsum("bkv,kvd->bd", z, codebook.float())
E = E.clone(); E[bidx, zs + t + 1] = fed.to(E.dtype)
final = head(out.hidden_states[-1][bidx, zs + m - 1]).view(B, K, 10) # head at z_T
return [final[j, batch[j].q].argmax().item() for j in range(B)]
def expected_answer(task, p, t, i, d):
row = list(p.rows[t]); row[i] = d
for _ in range(task.m - 1 - t):
row = task._step(row)
return row[p.q]
def run(ckpt, model_name, n, device):
model, head, codebook, q_emb, tok, did, K, cfg, tag = load(ckpt, model_name, device)
im = tok.convert_tokens_to_ids("<|im_end|>")
m = cfg["task_kwargs"]["m"]; task = LT.make_task(cfg["task"], k=K, m=m)
rng = random.Random(7)
probs = [task.sample(rng) for _ in range(n)]
# per-example patch: a random step t in 0..m-2, cell i, target digit d != current value
t_p, i_p, d_p = [], [], []
for p in probs:
t = rng.randrange(0, m - 1); i = rng.randrange(K)
cur = p.rows[t][i]; d = rng.choice([x for x in range(10) if x != cur])
t_p.append(t); i_p.append(i); d_p.append(d)
exp = np.array([expected_answer(task, probs[j], t_p[j], i_p[j], d_p[j]) for j in range(n)])
orig = np.array([task.answer(probs[j]) for j in range(n)])
preds = []
bs = 16
for s in tqdm(range(0, n, bs), desc=f"{tag} cell-patch"):
b = probs[s:s + bs]
preds += patched_answer(model, head, codebook, q_emb, tok, task, b, im, device,
t_p[s:s + bs], i_p[s:s + bs], d_p[s:s + bs])
preds = np.array(preds)
follows_ca = float((preds == exp).mean())
changed = exp != orig # patch actually moves the answer
follows_on_changed = float((preds[changed] == exp[changed]).mean())
follows_orig_on_changed = float((preds[changed] == orig[changed]).mean()) # ignores the patch?
dist = np.array([m - 1 - t for t in t_p]) # propagation distance
by_dist = {int(dd): float((preds[dist == dd] == exp[dist == dd]).mean()) for dd in sorted(set(dist.tolist()))}
print("=" * 70 + f"\nSINGLE-CELL PATCH {tag} K={K} m={m} n={n}\n" + "=" * 70, flush=True)
print(f" follows CA-propagated answer (all patches): {follows_ca:.3f}", flush=True)
print(f" on patches that CHANGE the answer (n={int(changed.sum())}): follows patched CA={follows_on_changed:.3f} "
f"follows ORIGINAL (ignores patch)={follows_orig_on_changed:.3f}", flush=True)
print(f" by propagation distance (steps from patch to read): { {k: round(v,3) for k,v in by_dist.items()} }", flush=True)
res = dict(tag=tag, K=K, m=m, n=n, follows_ca=follows_ca, n_changed=int(changed.sum()),
follows_on_changed=follows_on_changed, follows_orig_on_changed=follows_orig_on_changed,
by_distance=by_dist)
json.dump(res, open(f"{RES}/single_cellpatch_{tag}.json", "w"), indent=2)
fig, ax = plt.subplots(figsize=(6, 4))
ds = sorted(by_dist); ax.plot(ds, [by_dist[d] for d in ds], "o-", color="#2ca02c", label="follows patched CA")
ax.axhline(1 / 10, ls="--", color="grey", label="chance 0.10")
ax.set_xlabel("propagation distance (CA steps from patched cell to read-out)")
ax.set_ylabel("P(answer = CA-propagated value)"); ax.set_ylim(0, 1.02); ax.legend()
ax.set_title(f"{tag}: single-cell patch c_i(t):=d propagates correctly")
png = f"{RES}/single_cellpatch_{tag}.png"; fig.savefig(png, bbox_inches="tight", dpi=130)
print(png, flush=True)
return res
def run_sweep(ckpt, model_name, n_probs, device):
"""Systematic spot-check: patch EVERY (step t, cell i) site on a small set of problems, force a
changed value, and print the full per-patch detail (orig answer / CA-expected / model) for review."""
model, head, codebook, q_emb, tok, did, K, cfg, tag = load(ckpt, model_name, device)
im = tok.convert_tokens_to_ids("<|im_end|>")
m = cfg["task_kwargs"]["m"]; task = LT.make_task(cfg["task"], k=K, m=m)
rng = random.Random(11)
probs = [task.sample(rng) for _ in range(n_probs)]
sites = [(t, i) for t in range(m - 1) for i in range(K)] # all positions x all cells
batch, T, I, D, meta = [], [], [], [], []
for pi, p in enumerate(probs):
for (t, i) in sites:
cur = p.rows[t][i]; d = rng.choice([x for x in range(10) if x != cur])
batch.append(p); T.append(t); I.append(i); D.append(d)
meta.append((pi, t, i, cur, d, p.q, task.answer(p), expected_answer(task, p, t, i, d)))
preds = []
for s in range(0, len(batch), 16):
preds += patched_answer(model, head, codebook, q_emb, tok, task, batch[s:s + 16], im, device,
T[s:s + 16], I[s:s + 16], D[s:s + 16])
print("=" * 92 + f"\nSINGLE-CELL PATCH SWEEP {tag} K={K} m={m} {len(sites)} sites x {n_probs} problems = {len(meta)} patches\n" + "=" * 92, flush=True)
print(f"{'prob':>4} {'site':>8} {'c_i(t)->d':>10} {'q':>4} {'orig':>5} {'CA_exp':>7} {'model':>6} match", flush=True)
persite, ok_all = {}, []
for k, (pi, t, i, cur, d, q, orig, exp) in enumerate(meta):
mod = preds[k]; ok = (mod == exp); ok_all.append(ok); persite.setdefault((t, i), []).append(ok)
print(f"{pi:>4} {f's{t+1}.c{i+1}':>8} {f'{cur}->{d}':>10} {f'c{q+1}':>4} {orig:>5} {exp:>7} {mod:>6} {'OK' if ok else 'XX'}", flush=True)
print("-" * 92 + "\nper-site follows-CA (over the problems):", flush=True)
for (t, i) in sites:
v = persite[(t, i)]; print(f" s{t+1}.c{i+1}: {sum(v)}/{len(v)} = {sum(v)/len(v):.2f}", flush=True)
print(f"OVERALL follows CA-propagated: {sum(ok_all)}/{len(ok_all)} = {sum(ok_all)/len(ok_all):.3f}", flush=True)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--ckpt", required=True); ap.add_argument("--model", default="Qwen/Qwen3-8B")
ap.add_argument("--n", type=int, default=400)
ap.add_argument("--sweep", action="store_true"); ap.add_argument("--probs", type=int, default=7)
args = ap.parse_args()
if args.sweep:
run_sweep(args.ckpt, args.model, args.probs, "cuda")
else:
run(args.ckpt, args.model, args.n, "cuda")
if __name__ == "__main__":
main()
|