Download scripts/train_baseline.py from FluidInference/citenet-coreml: direct link, hf CLI and curl.
- Browser
- Download file 3.13 kB
-
https://huggingface.co/FluidInference/citenet-coreml/resolve/main/scripts/train_baseline.py
- Command line
-
hf download hf://FluidInference/citenet-coreml/scripts/train_baseline.py
-
curl -L -o train_baseline.py https://huggingface.co/FluidInference/citenet-coreml/resolve/main/scripts/train_baseline.py
3.13 kB
| """Same CiteNet architecture, trained instead of hand-written. | |
| random : random init, Adam + class-weighted BCE on aux_train (genuine vs phantom) | |
| handinit: start from the hand-written weights, same training | |
| Scored on the LePhantomCite eval split, and on the planted fakes in the demo brief | |
| (court mismatch / volume-year / state mismatch: kinds the train split's phantoms | |
| never show, since they are all invented series). | |
| """ | |
| import os, sys, copy | |
| import numpy as np, torch, torch.nn.functional as F | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from citenet import net as cn, text | |
| from lepc import load | |
| torch.manual_seed(0); np.random.seed(0) | |
| tr = [r for r in load("aux_train") if r["label"] != "other"] | |
| ev = load("eval") | |
| Xtr = torch.from_numpy(text.encode([r["cite"] for r in tr])); ytr = torch.tensor([r["label"] == "phantom" for r in tr], dtype=torch.float32) | |
| Xev = torch.from_numpy(text.encode([r["cite"] for r in ev])); lev = np.array([r["label"] for r in ev]) | |
| brief = open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "demo", "sample_brief.txt")).read() | |
| bc = [c for _, _, c in text.extract(brief)] | |
| hand, _ = cn.make() | |
| with torch.no_grad(): | |
| bl = (hand(torch.from_numpy(text.encode(bc)))[0][:, 0] >= 0.5).numpy() # hand-written labels = planted truth here | |
| Xb = torch.from_numpy(text.encode(bc)) | |
| def score(m, tag): | |
| with torch.no_grad(): | |
| p = m(Xev)[0][:, 0].numpy(); pb = m(Xb)[0][:, 0].numpy() | |
| f = p >= 0.5 | |
| rec = (f & (lev == "phantom")).sum(), (lev == "phantom").sum() | |
| ff = (f & (lev == "genuine")).sum(), (lev == "genuine").sum() | |
| fb = pb >= 0.5 | |
| ood = [c for c, t, y in zip(bc, bl, fb) if t and "th " not in c.split("(")[0]] | |
| caught = sum(1 for c, t, y in zip(bc, bl, fb) if t and y and "th " not in c.split("(")[0]) | |
| series = sum(1 for c, t, y in zip(bc, bl, fb) if t and y and "th " in c.split("(")[0]) | |
| fpb = sum(1 for t, y in zip(bl, fb) if y and not t) | |
| print(f"{tag:22s} eval phantoms {rec[0]}/{rec[1]} eval false flags {ff[0]}/{ff[1]} | brief: series fakes {series}/2, " | |
| f"other fakes {caught}/{len(ood)}, real flagged {fpb}/{int((~bl).sum())}") | |
| def train(m, steps=1500, lr=3e-3): | |
| m.train() | |
| opt = torch.optim.Adam(m.parameters(), lr=lr) | |
| pw = torch.tensor((1 - ytr.mean()) / ytr.mean()) | |
| for s in range(steps): | |
| idx = torch.randint(0, len(Xtr), (256,)) | |
| p = m(Xtr[idx])[0][:, 0].clamp(1e-6, 1 - 1e-6) | |
| logit = torch.log(p) - torch.log1p(-p) | |
| loss = F.binary_cross_entropy_with_logits(logit, ytr[idx], pos_weight=pw) | |
| opt.zero_grad(); loss.backward(); opt.step() | |
| return m.eval() | |
| score(hand, "hand-written (0 steps)") | |
| for seed in (0, 1, 2): | |
| torch.manual_seed(seed) | |
| rnd = cn.CiteNet(cn.build()[0]) | |
| for mod in rnd.modules(): | |
| if isinstance(mod, (torch.nn.Conv2d, torch.nn.Linear)): | |
| mod.reset_parameters() | |
| score(train(rnd), f"random init, seed {seed}") | |
| torch.manual_seed(0) | |
| score(train(copy.deepcopy(hand), lr=1e-4), "hand init + fine-tune") | |