One-time fresh-test evaluation of object pipelines
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8f126bc29a
commit
c2ac33767d
4 changed files with 135 additions and 2 deletions
|
|
@ -303,3 +303,17 @@ KNP, validation: `fc_v_plain` 81,0%, `fc_v_augB` 79,5%. Симулятор (2000
|
||||||
- Ошибка ожидания: условие `pgrep -f grid_v3_sparse.sh` находило саму команду ожидания, компьютер простаивал ~22:40–00:10.
|
- Ошибка ожидания: условие `pgrep -f grid_v3_sparse.sh` находило саму команду ожидания, компьютер простаивал ~22:40–00:10.
|
||||||
- `objects/fetch_fresh_test.py`: докачаны test 0700–0999 cat/dog (600, нигде не использовались) — для одной итоговой проверки.
|
- `objects/fetch_fresh_test.py`: докачаны test 0700–0999 cat/dog (600, нигде не использовались) — для одной итоговой проверки.
|
||||||
- `objects/spiking_pipeline.py`: точная пошаговая симуляция пиксели → слой 1 (K × 27×27 нейронов, целые веса ±127, смещение каждый активный шаг, порог, сброс 0, не ниже 0) → нейрон «собака − кошка» (веса общие для узора и четверти, задержка 1). Проба на 300 обучающих: линейный на точных счётчиках 63,3%, спайковый выход 63,0% (порог 4 × медиана входа за шаг).
|
- `objects/spiking_pipeline.py`: точная пошаговая симуляция пиксели → слой 1 (K × 27×27 нейронов, целые веса ±127, смещение каждый активный шаг, порог, сброс 0, не ниже 0) → нейрон «собака − кошка» (веса общие для узора и четверти, задержка 1). Проба на 300 обучающих: линейный на точных счётчиках 63,3%, спайковый выход 63,0% (порог 4 × медиана входа за шаг).
|
||||||
|
|
||||||
|
### 00:20 – 01:00: точная спайковая цепочка и однократная проверка на свежем тесте
|
||||||
|
- Точная симуляция `spiking_pipeline.py`, K=800 (`v3_k800_lin.npz`), 18 000 обучение + 1000 validation, 582 с: порог слоя 1 108,08 (99-й перцентиль) — линейный на точных счётчиках 71,0%, спайковый выход **71,6%** (lam 1e6, порог 4 × медиана); ~668 тыс. спайков слоя 1 на картинку. Порог 50 — 70,0%, порог 75 — 70,6% (не лучше).
|
||||||
|
- Свежий тест: CIFAR-10 JPEG test 0700–0999 на класс, 600 картинок, ранее не использовались (`objects/fetch_fresh_test.py`, `prepare_fresh_test.py`). Все настройки зафиксированы по validation до проверки; признаки D/E проверены на побайтное совпадение с сохранёнными. Скрипты `objects/prepare_fresh_knp.py`, `objects/final_fresh_test.py`; результаты `~/run/cats-dogs/final_fresh_test.json`, прогоны `fresh_D_v3`, `fresh_E_v2`.
|
||||||
|
|
||||||
|
| Вариант | validation | свежий тест (600) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| E: v2, sparse HOG + колонки STDP (t4000, c20) | 62,6% | 62,5% |
|
||||||
|
| D: выученные K=512 top-256 + колонки STDP, наказание 0,012 (t1600), KNP | 64,3% | 67,0% |
|
||||||
|
| C: полностью спайковая, точная симуляция (слой 1 + нейрон «собака − кошка») | 71,6% | 68,3% |
|
||||||
|
| B: K=800 без деления на контраст, ridge (без спайков) | 77,1% | 76,5% |
|
||||||
|
| A: K=800 с делением на контраст, ridge (без спайков) | 78,4% | 77,2% |
|
||||||
|
|
||||||
|
95% интервал при n=600 около ±3,7 п.п. **Свежий тест теперь просмотрен**; все test-картинки кошек и собак CIFAR (0–999) использованы, для новой независимой оценки нужен другой источник.
|
||||||
|
|
|
||||||
72
objects/final_fresh_test.py
Normal file
72
objects/final_fresh_test.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""One-time evaluation on the fresh cats/dogs test (600 images). Settings were fixed on validation before.
|
||||||
|
|
||||||
|
A: learned features K 800 with contrast division, standardised, ridge lam 300 (no spikes; upper reference).
|
||||||
|
B: K 800 without contrast division (folds into neuron weights), alpha 0.25, ridge lam 300.
|
||||||
|
C: fully spiking, exact step-by-step simulation (spiking_pipeline.py): layer-1 threshold 108.08, readout ridge
|
||||||
|
lam 1e6 on exact layer-1 spike counts, readout neuron threshold 4 × median per-step input (unit from validation).
|
||||||
|
|
||||||
|
usage: ~/anvenv/bin/python final_fresh_test.py
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, "/mnt/c/knp/objects")
|
||||||
|
import kmeans_features as kf # noqa: E402
|
||||||
|
import spiking_pipeline as sp # noqa: E402
|
||||||
|
|
||||||
|
root = "/home/int/run/cats-dogs"
|
||||||
|
fresh = np.load(f"{root}/data-v3/fresh_test.npz")
|
||||||
|
xf, yf = fresh["rgb"], fresh["labels"].astype(int)
|
||||||
|
tr, va = np.load(f"{root}/data-v3/train.npz"), np.load(f"{root}/data-v3/val.npz")
|
||||||
|
yt = np.r_[tr["labels"], tr["labels"]].astype(int)
|
||||||
|
yv = va["labels"].astype(int)
|
||||||
|
res = {}
|
||||||
|
|
||||||
|
|
||||||
|
def ridge(f, y, lam):
|
||||||
|
f = np.hstack([f, np.ones((len(f), 1), np.float32)])
|
||||||
|
return np.linalg.solve(f.T @ f + lam * np.eye(f.shape[1]), f.T @ (np.eye(2)[y] * 2 - 1))
|
||||||
|
|
||||||
|
|
||||||
|
def acc(w, f, y):
|
||||||
|
f = np.hstack([f, np.ones((len(f), 1), np.float32)])
|
||||||
|
return 100 * ((f @ w).argmax(1) == y).mean()
|
||||||
|
|
||||||
|
|
||||||
|
# A
|
||||||
|
d = np.load(f"{root}/v3_k800.npz")
|
||||||
|
model = {k: d[k] for k in ("mean", "zca", "d")}
|
||||||
|
kf.CONTRAST_DIVIDE = True
|
||||||
|
mu, sd = d["Ft"].mean(0), d["Ft"].std(0) + 1e-3
|
||||||
|
w = ridge((d["Ft"] - mu) / sd, yt, 300)
|
||||||
|
res["A"] = {"val": acc(w, (d["Fv"] - mu) / sd, yv), "fresh": acc(w, (kf.encode(xf, model) - mu) / sd, yf)}
|
||||||
|
print("A", res["A"], flush=True)
|
||||||
|
|
||||||
|
# B
|
||||||
|
d = np.load(f"{root}/v3_k800_lin.npz")
|
||||||
|
model = {k: d[k] for k in ("mean", "zca", "d")}
|
||||||
|
kf.CONTRAST_DIVIDE = False
|
||||||
|
ft = np.vstack([kf.encode(tr["rgb"], model, alpha=0.25), kf.encode(tr["rgb"][:, :, :, ::-1], model, alpha=0.25)])
|
||||||
|
mu, sd = ft.mean(0), ft.std(0) + 1e-3
|
||||||
|
w = ridge((ft - mu) / sd, yt, 300)
|
||||||
|
res["B"] = {"val": acc(w, (kf.encode(va["rgb"], model, alpha=0.25) - mu) / sd, yv),
|
||||||
|
"fresh": acc(w, (kf.encode(xf, model, alpha=0.25) - mu) / sd, yf)}
|
||||||
|
print("B", res["B"], flush=True)
|
||||||
|
|
||||||
|
# C
|
||||||
|
s = np.load(f"{root}/spiking_v3_k800.npz")
|
||||||
|
ct, sv = s["ct"], s["sv"]
|
||||||
|
f = lambda c: np.hstack([c.astype(np.float32), np.ones((len(c), 1), np.float32)])
|
||||||
|
sol = np.linalg.solve(f(ct).T @ f(ct) + 1e6 * np.eye(ct.shape[1] + 1), f(ct).T @ (s["yt"] * 2 - 1.0))
|
||||||
|
rq = np.rint(sol / np.abs(sol).max() * 255).astype(np.int64)
|
||||||
|
bias = int(np.rint(rq[-1] / sp.ACTIVE))
|
||||||
|
unit = float(np.median(np.abs(sv[:, :sp.ACTIVE].reshape(-1, sv.shape[-1]) @ rq[:-1] + bias))) + 1
|
||||||
|
thr = 4 * unit
|
||||||
|
sf, tf = sp.layer1_counts(xf, s["wq"], s["bq"], float(s["thr1"]), per_step=True)
|
||||||
|
res["C"] = {"val": 100 * (sp.readout_neuron(sv, rq[:-1], bias, thr) == yv).mean(),
|
||||||
|
"fresh": 100 * (sp.readout_neuron(sf, rq[:-1], bias, thr) == yf).mean(),
|
||||||
|
"layer1_spikes_per_image": int(tf.mean()), "readout_threshold": thr}
|
||||||
|
print("C", res["C"], flush=True)
|
||||||
|
json.dump(res, open(f"{root}/final_fresh_test.json", "w"), indent=1)
|
||||||
47
objects/prepare_fresh_knp.py
Normal file
47
objects/prepare_fresh_knp.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
"""KNP input files for the one-time fresh-test evaluation (600 images, test 0700–0999 per class).
|
||||||
|
|
||||||
|
D: learned features (K 512 model from make_knp_features.py, same gain), top-256 per image; training part =
|
||||||
|
the first 18000 rows of kmeans_top256_validation.bin (9000 images + mirror copies).
|
||||||
|
E: v2 sparse HOG (cats-dogs/features.py, descriptor(sparse=True)); training part = the first 4000 rows of
|
||||||
|
features-v2/sparse_hog_validation.bin (v2 training set).
|
||||||
|
|
||||||
|
usage: ~/anvenv/bin/python prepare_fresh_knp.py
|
||||||
|
"""
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, "/mnt/c/knp/objects")
|
||||||
|
sys.path.insert(0, "/mnt/c/knp/cats-dogs")
|
||||||
|
import kmeans_features as kf # noqa: E402
|
||||||
|
from features import descriptor # noqa: E402
|
||||||
|
|
||||||
|
root = "/home/int/run/cats-dogs"
|
||||||
|
fresh = np.load(f"{root}/data-v3/fresh_test.npz")
|
||||||
|
labels = "\n".join(map(str, fresh["labels"])) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def write(path, train_bin, train_target, n_train, width, test):
|
||||||
|
x = np.fromfile(train_bin, np.uint8).reshape(-1, width)[:n_train]
|
||||||
|
y = open(train_target).read().split()[:n_train]
|
||||||
|
np.concatenate([x, test]).tofile(path + ".bin")
|
||||||
|
open(path + ".target", "w").write("\n".join(y) + "\n" + labels)
|
||||||
|
print(path, n_train, "+", len(test))
|
||||||
|
|
||||||
|
|
||||||
|
# D
|
||||||
|
kf.CONTRAST_DIVIDE = False
|
||||||
|
m = dict(np.load(f"{root}/features-v3/kmeans_model.npz"))
|
||||||
|
f = kf.encode(fresh["rgb"], m, alpha=0.25)
|
||||||
|
q = np.clip(np.rint(f / m["gain"] * 255), 0, 255).astype(np.uint8)
|
||||||
|
keep = np.argpartition(q, -256, axis=1)[:, -256:]
|
||||||
|
s = np.zeros_like(q)
|
||||||
|
np.put_along_axis(s, keep, np.take_along_axis(q, keep, 1), 1)
|
||||||
|
write(f"{root}/features-v3/kmeans_top256_fresh", f"{root}/features-v3/kmeans_top256_validation.bin",
|
||||||
|
f"{root}/features-v3/kmeans_top256_validation.target", 18000, 2048, s)
|
||||||
|
|
||||||
|
# E
|
||||||
|
h = descriptor(fresh["rgb"].reshape(-1, 3, 1024), sparse=True)
|
||||||
|
write(f"{root}/features-v2/sparse_hog_fresh", f"{root}/features-v2/sparse_hog_validation.bin",
|
||||||
|
f"{root}/features-v2/sparse_hog_validation.target", 4000, 2048, h)
|
||||||
|
|
@ -131,7 +131,7 @@ def main():
|
||||||
np.savez(a.out, ct=ct, sv=sv, yt=yy, yv=va["labels"], thr1=a.thr1, wq=wq, bq=bq)
|
np.savez(a.out, ct=ct, sv=sv, yt=yy, yv=va["labels"], thr1=a.thr1, wq=wq, bq=bq)
|
||||||
f = lambda c: np.hstack([c.astype(np.float32), np.ones((len(c), 1), np.float32)])
|
f = lambda c: np.hstack([c.astype(np.float32), np.ones((len(c), 1), np.float32)])
|
||||||
res = {}
|
res = {}
|
||||||
for lam in [1e3, 1e4, 1e5, 1e6]:
|
for lam in [1e5, 1e6, 1e7]:
|
||||||
sol = np.linalg.solve(f(ct).T @ f(ct) + lam * np.eye(ct.shape[1] + 1), f(ct).T @ (yy * 2 - 1.0))
|
sol = np.linalg.solve(f(ct).T @ f(ct) + lam * np.eye(ct.shape[1] + 1), f(ct).T @ (yy * 2 - 1.0))
|
||||||
acc = 100 * (((f(cv) @ sol) > 0) == va["labels"]).mean()
|
acc = 100 * (((f(cv) @ sol) > 0) == va["labels"]).mean()
|
||||||
rq = np.rint(sol / np.abs(sol).max() * 255).astype(np.int64)
|
rq = np.rint(sol / np.abs(sol).max() * 255).astype(np.int64)
|
||||||
|
|
@ -139,7 +139,7 @@ def main():
|
||||||
bias = int(np.rint(rq[-1] / ACTIVE))
|
bias = int(np.rint(rq[-1] / ACTIVE))
|
||||||
# Readout threshold relative to the typical size of its per-step input.
|
# Readout threshold relative to the typical size of its per-step input.
|
||||||
unit = float(np.median(np.abs(sv[:, :ACTIVE].reshape(-1, sv.shape[-1]) @ rq[:-1] + bias))) + 1
|
unit = float(np.median(np.abs(sv[:, :ACTIVE].reshape(-1, sv.shape[-1]) @ rq[:-1] + bias))) + 1
|
||||||
spk = {q: 100 * (readout_neuron(sv, rq[:-1], bias, q * unit) == va["labels"]).mean() for q in (0.5, 1, 2, 4, 8)}
|
spk = {q: 100 * (readout_neuron(sv, rq[:-1], bias, q * unit) == va["labels"]).mean() for q in (2, 3, 4, 5, 6)}
|
||||||
res[lam] = {"linear": acc, "spiking": spk, "unit": unit}
|
res[lam] = {"linear": acc, "spiking": spk, "unit": unit}
|
||||||
print(f"lam {lam:g}: linear on exact layer-1 counts {acc:.1f}%; spiking readout by threshold "
|
print(f"lam {lam:g}: linear on exact layer-1 counts {acc:.1f}%; spiking readout by threshold "
|
||||||
+ ", ".join(f"{q}×: {v:.1f}%" for q, v in spk.items()) + f" (unit {unit:.0f})", flush=True)
|
+ ", ".join(f"{q}×: {v:.1f}%" for q, v in spk.items()) + f" (unit {unit:.0f})", flush=True)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue