AUBIN-12B / code /learn_skill2.py
emrevrg's picture
AUBIN-Learn: instant self-learning memory, fast skills, self-calibration + reports
74311c3 verified
Raw History Blame Contribute Delete
6.85 kB
"""AUBIN-Learn Beceri-2: kaynakların AÇIK eğitim bölümünden (Kev train + kev_augment ek verisi, Kev'in hiçbir kümesiyle
çakışmaz) öğrenilen ince-ayarlı kodlayıcı becerisi. Tek kodlayıcı + (kaynak, soru) başına çıkış dilimi; girdi = soru + seçenek
metinleri + durum. Eğitim ayarları ÖNCEDEN sabit (dev/test'e bakılmaz); kev_dev yalnız sonra birleştirme ağırlığı seçiminde,
kev_test / kev_transfer_test yalnız rapor. Çıktı: her küme için soru sırasına hizalı olasılıklar (seçenek ADIyla eşlenmiş).
KEV_EXTRA_TRAIN=kev_extra.jsonl python learn_skill2.py --work /tmp/kev --model microsoft/deberta-v3-base --out skill2.json
"""
import argparse, json, math, os, random, sys, time
import numpy as np
import torch
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import kevdata
from kev_llm import items
SUITES = ("kev_dev", "kev_test", "kev_transfer_test", "kev_cal")
def gkey(it):
return it["src"] + "|" + it["key"].rsplit("/", 1)[1]
def text(it):
# soru (kısa; MNLI hipotezi / BoolQ sorusu burada) → durum → seçenekler: kesilme yalnız seçenek listesinden olur
# (ilk sürümde seçenekler öndeydi; banking77'de 77 seçenek durumu tamamen kesiyordu)
return f"{it['q']}\n\n{it['state']}\n\nOptions: " + " ; ".join(it["texts"][:26])
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--work", default="/tmp/kev"); ap.add_argument("--model", default="microsoft/deberta-v3-base")
ap.add_argument("--epochs", type=float, default=3); ap.add_argument("--bs", type=int, default=32)
ap.add_argument("--lr", type=float, default=3e-5); ap.add_argument("--max_len", type=int, default=256)
ap.add_argument("--seed", type=int, default=0); ap.add_argument("--out", default="skill2.json")
ap.add_argument("--micro", type=int, default=0) # >0: mikro-yığın boyu (gradyan biriktirme + checkpointing)
a = ap.parse_args()
random.seed(a.seed); torch.manual_seed(a.seed)
K = kevdata.load(a.work)
tr = items(K["kev_train"])
G = {}
for it in tr:
G.setdefault(gkey(it), set()).add(str(it["keys"][it["y"]]))
cnt = {}
for it in tr:
cnt[gkey(it)] = cnt.get(gkey(it), 0) + 1
G = {g: sorted(v) for g, v in G.items() if len(v) >= 2 and cnt[g] >= 50}
off, tot = {}, 0
for g in sorted(G):
off[g] = tot; tot += len(G[g])
tr = [it for it in tr if gkey(it) in G]
print({"train_items": len(tr), "groups": len(G), "slots": tot}, flush=True)
from transformers import AutoTokenizer, AutoModel
tok = AutoTokenizer.from_pretrained(a.model)
enc = AutoModel.from_pretrained(a.model, dtype=torch.float32).cuda().float() # ana ağırlıklar fp32 (AMP fp16 yalnız hesapta)
if a.micro:
enc.gradient_checkpointing_enable()
head = torch.nn.Linear(enc.config.hidden_size, tot).cuda()
params = list(enc.parameters()) + list(head.parameters())
opt = torch.optim.AdamW([{"params": enc.parameters(), "lr": a.lr}, {"params": head.parameters(), "lr": a.lr * 10}], weight_decay=0.01)
steps = int(math.ceil(len(tr) / a.bs) * a.epochs)
sched = torch.optim.lr_scheduler.LambdaLR(opt, lambda s: min(1.0, s / max(1, int(0.06 * steps))) * max(0.0, (steps - s) / steps))
scaler = torch.cuda.amp.GradScaler()
def batch(its):
b = tok([text(x) for x in its], truncation=True, max_length=a.max_len, padding=True, return_tensors="pt")
return {k: v.cuda() for k, v in b.items()}
def logits(b, its):
h = enc(**b).last_hidden_state[:, 0]
z = head(h).float()
m = torch.full_like(z, -1e4)
for i, x in enumerate(its):
g = gkey(x); m[i, off[g]: off[g] + len(G[g])] = 0
return z + m
t0 = time.time(); s = 0; enc.train()
while s < steps:
order = list(range(len(tr))); random.shuffle(order)
for i in range(0, len(order), a.bs):
if s >= steps:
break
its = [tr[j] for j in order[i: i + a.bs]]
opt.zero_grad(set_to_none=True)
for j in range(0, len(its), a.micro or len(its)): # gradyan biriktirme (büyük kodlayıcılar T4'e sığsın)
mb = its[j: j + (a.micro or len(its))]
y = torch.tensor([off[gkey(x)] + G[gkey(x)].index(str(x["keys"][x["y"]])) for x in mb]).cuda()
with torch.autocast("cuda", dtype=torch.float16):
z = logits(batch(mb), mb)
loss = torch.nn.functional.cross_entropy(z, y) * len(mb) / len(its)
scaler.scale(loss).backward()
scaler.unscale_(opt); torch.nn.utils.clip_grad_norm_(params, 1.0)
scaler.step(opt); scaler.update(); sched.step(); s += 1
if s % 200 == 0:
print(f"adım {s}/{steps} kayıp {loss.item():.4f} {time.time() - t0:.0f}s", flush=True)
enc.eval(); R = {"protocol": __doc__.split(" KEV")[0].strip(), "model": a.model, "epochs": a.epochs, "lr": a.lr,
"max_len": a.max_len, "train_items": len(tr), "train_minutes": round((time.time() - t0) / 60, 1), "suites": {}}
for sname in SUITES:
if sname not in K:
continue
ev = items(K[sname]); P = []
with torch.no_grad():
for i in range(0, len(ev), 64):
its = ev[i: i + 64]
ok = [x for x in its if gkey(x) in G]
pr = {}
if ok:
with torch.autocast("cuda", dtype=torch.float16):
z = logits(batch(ok), ok)
for x, row in zip(ok, torch.log_softmax(z, -1).exp().cpu().numpy()):
g = gkey(x); pos = {c: j for j, c in enumerate(G[g])}
p = np.array([row[off[g] + pos[str(k)]] if str(k) in pos else 0.0 for k in x["keys"]], dtype=np.float64)
pr[x["key"]] = (p / p.sum()).round(5).tolist() if p.sum() > 0 else None
P += [pr.get(x["key"]) for x in its]
acc = [int(np.argmax(p)) == x["y"] for p, x in zip(P, ev) if p is not None]
bys = {}
for p, x in zip(P, ev):
if p is not None:
bys.setdefault(x["src"], []).append(int(np.argmax(p)) == x["y"])
R["suites"][sname] = {"keys": [x["key"] for x in ev], "p": P, "covered": len(acc), "n": len(ev),
"skill_acc_covered": round(float(np.mean(acc)), 4) if acc else None,
"by_source": {k: [len(v), round(float(np.mean(v)), 4)] for k, v in sorted(bys.items())}}
print(sname, R["suites"][sname]["covered"], R["suites"][sname]["skill_acc_covered"], R["suites"][sname]["by_source"], flush=True)
json.dump(R, open(a.out, "w"))
print("BITTI", a.out, flush=True)
if __name__ == "__main__":
main()