"""Builds altai_viz.html: embeds trained weights, sample test images and full-test statistics into app.html. usage: python3 export_data.py "||" ... [--readout r.npz] --readout: stroke features + spiking readout network (npz with w, thr, kernels, scale from readout_coarseA_page.npz); its test accuracy is computed here with the same code as the experiments. """ import base64 import json import sys from pathlib import Path import numpy as np from altai_sim import CLASSES, Sim, load_dump, load_images, predict GALLERY_RANDOM, GALLERY_HARD = 60, 30 def b64(arr, dtype): return base64.b64encode(np.ascontiguousarray(arr, dtype=dtype).tobytes()).decode() def main(): args = sys.argv[1:] readout = None if "--readout" in args: i = args.index("--readout") readout = args[i + 1] del args[i:i + 2] out, images, labels_path, *specs = args total = len(open(labels_path).read().split()) imgs, labels = load_images(images, labels_path, total - 10000, 10000) models, preds = [], {} for spec in specs: name, dump, inhib = spec.split("|") w, thr = load_dump(dump) sim = Sim(w, thr, float(inhib)) p = np.array([predict(sim.run_image(img)[0]) for img in imgs]) preds[name] = p confusion = np.zeros((CLASSES, CLASSES + 1), dtype=int) # last column: no answer for lab, pr in zip(labels, p): confusion[lab, pr if pr >= 0 else CLASSES] += 1 models.append({ "name": name, "inhib": float(inhib), "col": len(thr) // CLASSES, "thr": thr.astype(int).tolist(), "w": b64(w.T, np.int8), # [neuron][pixel] "confusion": confusion.tolist(), "accuracy": float((p == labels).mean()), }) print(name, f"{(p == labels).mean():.4f}") rng = np.random.default_rng(7) first, last = list(preds.values())[0], list(preds.values())[-1] fixed = np.flatnonzero((first != labels) & (last == labels)) broken = np.flatnonzero(last != labels) pick = list(rng.choice(10000, GALLERY_RANDOM, replace=False)) pick += list(rng.choice(fixed, min(GALLERY_HARD, len(fixed)), replace=False)) pick += list(rng.choice(broken, min(GALLERY_HARD // 2, len(broken)), replace=False)) pick = sorted(set(int(i) for i in pick)) extra = {} if readout: sys.path.insert(0, "/mnt/c/knp/features") sys.path.insert(0, "/mnt/c/knp/readout") import stroke_features as sf import spiking_readout as sr r = np.load(readout) sf.use_preset("coarse") sf.P["scale"] = float(r["scale"]) p = sr.spiking_predict(sf.features(imgs.reshape(-1, 28, 28)), r["w"], float(r["thr"])) confusion = np.zeros((CLASSES, CLASSES + 1), dtype=int) for lab, pr in zip(labels, p): confusion[lab, pr if pr >= 0 else CLASSES] += 1 extra["readout"] = {"name": "Штрихи + спайковый выход", "kernels": sf.KERNELS.astype(float).round(6).tolist(), "scale": float(r["scale"]), "thr": float(r["thr"]), "w": r["w"].astype(int).tolist(), "confusion": confusion.tolist(), "accuracy": float((p == labels).mean())} print("readout", f"{(p == labels).mean():.4f}") data = { **extra, "models": models, "gallery": {"index": pick, "labels": labels[pick].tolist(), "pixels": b64(imgs[pick], np.uint8)}, } html = (Path(__file__).parent / "app.html").read_text(encoding="utf-8") Path(out).write_text(html.replace("/*DATA*/null", json.dumps(data, separators=(",", ":"))), encoding="utf-8") print("written", out, len(pick), "gallery images") if __name__ == "__main__": main()