AUBIN-E4B-Control / code /learn_skill.py
emrevrg's picture
AUBIN-Learn: instant self-learning memory, fast skills, self-calibration + reports
5daf54a verified
Raw History Blame Contribute Delete
8.13 kB
"""AUBIN-Learn hızlı beceri (Norovox SkillLibrary'nin karar modeli karşılığı): bellekteki vakalardan kaynak-başına saniyeler içinde
öğrenilen doğrusal sınıflandırıcı (gömme → etiket ADI). Model + beceri log-doğrusal birleşir; ağırlık ve düzenlileştirme
(C) YALNIZ kev_dev'de seçilir, kev_test / kev_transfer_test bir kez raporlanır. Bellek = Kev train + ek veri (test asla girmez).
python learn_skill.py --pack model_lp.json.gz --emb emb_bge.npz --out learn_skill.json
"""
import argparse, glob, gzip, json, os, sys, time
import numpy as np
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import kevdata
from kev_llm import items
from aubin.learn import fuse
WGRID = [0.0, 0.1, 0.2, 0.35, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0]
def lsm(x):
x = np.asarray(x, dtype=np.float64); return x - np.logaddexp.reduce(x)
class SoftmaxProbe:
"""Bağımlılıksız çok sınıflı lojistik regresyon (numpy, L2 = 1/C, Adam, tam yığın). sklearn arayüzünün alt kümesi."""
def __init__(self, C=1.0, max_iter=300, lr=0.05):
self.C, self.max_iter, self.lr = C, max_iter, lr
def fit(self, X, y):
self.classes_ = np.array(sorted(set(y)))
idx = {c: i for i, c in enumerate(self.classes_)}
Y = np.zeros((len(y), len(self.classes_)), dtype=np.float32); Y[np.arange(len(y)), [idx[c] for c in y]] = 1
X = np.asarray(X, dtype=np.float32); n, d = X.shape
W = np.zeros((d, len(self.classes_)), dtype=np.float32); b = np.zeros(len(self.classes_), dtype=np.float32)
mW, vW, mb, vb = np.zeros_like(W), np.zeros_like(W), np.zeros_like(b), np.zeros_like(b)
lam = 1.0 / (self.C * n)
for t in range(1, self.max_iter + 1):
Z = X @ W + b; Z -= Z.max(1, keepdims=True); P = np.exp(Z); P /= P.sum(1, keepdims=True)
G = (P - Y) / n
gW, gb = X.T @ G + lam * W, G.sum(0)
for g, m, v, p in ((gW, mW, vW, W), (gb, mb, vb, b)):
m *= 0.9; m += 0.1 * g; v *= 0.999; v += 0.001 * g * g
p -= self.lr * (m / (1 - 0.9 ** t)) / (np.sqrt(v / (1 - 0.999 ** t)) + 1e-8)
self.W, self.b = W, b
return self
def predict_proba(self, X):
Z = np.asarray(X, dtype=np.float32) @ self.W + self.b; Z -= Z.max(1, keepdims=True); P = np.exp(Z)
return P / P.sum(1, keepdims=True)
def fit_skills(tr, VP, C):
"""Kaynak-başına çok sınıflı lojistik regresyon (etiket adları). Dönüş: {kaynak: (model, sınıflar, süre_ms)}."""
LogisticRegression = SoftmaxProbe
by = {}
for i, x in enumerate(tr):
by.setdefault(x["src"], []).append(i)
out = {}
for s, idx in by.items():
y = [str(tr[i]["keys"][tr[i]["y"]]) for i in idx]
if len(set(y)) < 2 or len(idx) < 50:
continue
t0 = time.perf_counter()
m = LogisticRegression(C=C)
m.fit(VP[idx], y)
out[s] = (m, list(m.classes_), (time.perf_counter() - t0) * 1e3)
return out
def skill_probs(skills, its, V):
P = []
for it, v in zip(its, V):
sk = skills.get(it["src"])
if sk is None:
P.append(None); continue
m, cls, _ = sk
pr = m.predict_proba(v[None])[0]
pos = {c: j for j, c in enumerate(cls)}
p = np.array([pr[pos[str(k)]] if str(k) in pos else 0.0 for k in it["keys"]], dtype=np.float64)
P.append(p / p.sum() if p.sum() > 0 else None)
return P
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--work", default="/tmp/kev"); ap.add_argument("--emb", default="")
ap.add_argument("--pack", required=True); ap.add_argument("--out", default="learn_skill.json")
a = ap.parse_args()
emb = a.emb or (glob.glob("/kaggle/input/**/" + os.environ.get("AUBIN_EMB_FILE", "emb_bge.npz"), recursive=True) or [""])[0]
Z = np.load(emb)
K = kevdata.load(a.work); tr = items(K["kev_train"])
VP = Z["VP"].astype(np.float32)
assert len(VP) == len(tr), f"gömme/bellek boyu uyuşmuyor {len(VP)} != {len(tr)}"
pack = json.load(gzip.open(a.pack, "rt", encoding="utf-8"))
ev = {s: items(K[s]) for s in ("kev_dev", "kev_test", "kev_transfer_test")}
import random as _r
_c = items(K["kev_cal"]); _r.Random(0).shuffle(_c); ev["cal300"] = _c[:300] # kev_dev'i eksik üyeler için ayar kümesi
VE = {s: Z["VE_" + s].astype(np.float32) for s in ev}
R = {"protocol": __doc__.split(" python")[0].strip(), "memory_items": len(tr), "C": {}, "runs": {}}
SK = {}
for C in (0.3, 1.0, 3.0, 10.0):
t0 = time.time(); sk = fit_skills(tr, VP, C)
SK[C] = {s: skill_probs(sk, ev[s], VE[s]) for s in ev}
R["C"][str(C)] = {"fit_seconds_total": round(time.time() - t0, 1),
"fit_ms_per_source": {s: round(v[2], 1) for s, v in sk.items()},
"skill_only_dev_acc": round(float(np.mean([int(np.argmax(p)) == it["y"] for p, it in zip(SK[C]["kev_dev"], ev["kev_dev"]) if p is not None])), 4)}
print("C", C, R["C"][str(C)]["skill_only_dev_acc"], R["C"][str(C)]["fit_seconds_total"], "s", flush=True)
for rn, rp in pack.items():
D = "kev_dev" if "kev_dev" in rp["suites"] else ("cal300" if "cal300" in rp["suites"] else None)
if D is None:
continue
T = rp.get("T", 1.0)
L = {s: [np.asarray(x) for x in rp["suites"][s]["lp"]] for s in ev if s in rp["suites"]}
def rows(s, C, wsrc):
return [(fuse(lp, p, wsrc.get(it["src"], wsrc.get("*", 0.0)), T), it["y"], it["src"])
for lp, p, it in zip(L[s], SK[C][s], ev[s])]
nll = lambda rr: float(np.mean([-r[0][r[1]] for r in rr]))
acc = lambda rr: float(np.mean([int(np.argmax(r[0])) == r[1] for r in rr]))
bestC, bestw = min(((C, w) for C in SK for w in WGRID), key=lambda cw: nll(rows(D, cw[0], {"*": cw[1]})))
gated = os.environ.get("AUBIN_SKILL_GATE", "1") == "1"
wsrc = {"*": 0.0 if gated else bestw} # temkinli: kanıtlanmayan kaynakta beceri KAPALI
for s in sorted({it["src"] for it in ev[D]}):
idx = [i for i, it in enumerate(ev[D]) if it["src"] == s]
if len(idx) < 30:
continue
def sub(w):
rr = rows(D, bestC, {"*": w})
return float(np.mean([-rr[i][0][rr[i][1]] for i in idx])), sum(int(np.argmax(rr[i][0])) == rr[i][1] for i in idx)
w_nll = min(WGRID, key=lambda w: (sub(w)[0], abs(w - bestw)))
if gated: # kapı: dev'de doğruluk ≥ +2 soru VE log-kayıp düşmeli; yoksa 0
(n0, a0), (n1, a1) = sub(0.0), sub(w_nll)
wsrc[s] = w_nll if (a1 >= a0 + 2 and n1 < n0) else 0.0
else:
wsrc[s] = w_nll
out = {"selected_on_dev": {"C": bestC, "w": wsrc, "gated": gated, "dev_split": D}}
for s in ("kev_dev", "kev_test", "kev_transfer_test"):
if s not in L:
continue
base, fu = rows(s, bestC, {"*": 0.0}), rows(s, bestC, wsrc)
bys = {}
for (b, f) in zip(base, fu):
bys.setdefault(b[2], [[], []]); bys[b[2]][0].append(int(np.argmax(b[0])) == b[1]); bys[b[2]][1].append(int(np.argmax(f[0])) == f[1])
sk_only = [int(np.argmax(p)) == it["y"] for p, it in zip(SK[bestC][s], ev[s]) if p is not None]
out[s] = {"model": round(acc(base), 4), "fused": round(acc(fu), 4), "model_nll": round(nll(base), 4), "fused_nll": round(nll(fu), 4),
"skill_only_when_available": round(float(np.mean(sk_only)), 4) if sk_only else None, "n": len(base),
"by_source": {k: [len(v[0]), round(float(np.mean(v[0])), 4), round(float(np.mean(v[1])), 4)] for k, v in sorted(bys.items())}}
R["runs"][rn] = out
print(rn, {s: (out[s]["model"], out[s]["fused"]) for s in ("kev_test", "kev_transfer_test") if s in out}, flush=True)
json.dump(R, open(a.out, "w"), indent=1)
print("BITTI", a.out, flush=True)
if __name__ == "__main__":
main()