laya-ad / code /scripts /eval_v22_vs_v21.py
x6nux's picture
laya-ad v2.2: six precision dirs
e2492ec verified
Raw History Blame Contribute Delete
3.93 kB
#!/usr/bin/env python3
"""v2.2 vs v2.1 四头模型对比评测:teacher_holdout / earlier_val / gmeow_val128。
gmeow_val128 是 v2.1 训练时没见过的 gmeow 留出切片(128 条,来自 ads_final.jsonl
与 data/cleaned/gmeow/val_split_texts.jsonl 的交集),专测 gmeow 语料带来的增益。
"""
import argparse
import json
import sys
from pathlib import Path
import numpy as np
import torch
from transformers import AutoTokenizer
sys.path.insert(0, str(Path(__file__).resolve().parent))
from dual_head_model import load_dual_head_any # noqa: E402
from train_multi_head import evaluate, tokenize # noqa: E402
KINDS = ["crypto", "gambling", "porn", "porn_bait", "promo", "scam", "spam_flood"]
def load(p):
return [json.loads(l) for l in open(p, encoding="utf-8") if l.strip()]
def gmeow_val_rows(ads_path, val_texts_path):
hold = {r["text"] for r in load(val_texts_path)}
rows = []
for r in load(ads_path):
if r.get("text") not in hold or r.get("kind") not in KINDS:
continue
sev = r.get("severity")
rows.append({
"text": r["text"], "label_bin": 0,
"label_kind": KINDS.index(r["kind"]),
"label_scope": 0 if r.get("scope") == "account" else 1,
"label_sev": int(round(sev)) if isinstance(sev, (int, float)) and 0 <= sev <= 3 else -100,
})
return rows
def eval_model(model_dir, sets, max_len=384, bs=64):
tok = AutoTokenizer.from_pretrained(model_dir)
model = load_dual_head_any(model_dir, device="cuda", dtype=torch.bfloat16)
out = {}
for name, rows in sets.items():
ids, am = tokenize(rows, tok, max_len)
m, pb, pk, ps, pv = evaluate(
model, rows, ids, am,
[r["label_bin"] for r in rows], [r["label_kind"] for r in rows],
[r["label_scope"] for r in rows], [r["label_sev"] for r in rows], bs, max_len)
vidx = [i for i, r in enumerate(rows) if r["label_sev"] >= 0]
if vidx:
y = np.array([rows[i]["label_sev"] for i in vidx])
exp = (pv[vidx] * np.arange(4)).sum(1)
m["sev_expected_mae"] = round(float(np.abs(exp - y).mean()), 4)
out[name] = m
print(f" {name:<16} acc={m['binary_acc']*100:6.2f}% adF1={m['ad_f1']:.3f} "
f"kind={m.get('kind_acc',0)*100:6.2f}% core={m.get('kind_core_f1',0):.3f} "
f"scope={m.get('scope_acc',0)*100:6.2f}% sevMAE={m.get('sev_expected_mae')}", flush=True)
del model
torch.cuda.empty_cache()
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--v22", default="outputs/ad-4head-v22/best")
ap.add_argument("--v21", default="outputs/ad-4head-teacher-heads/best")
ap.add_argument("--teacher", default="data/optimized_4h_v22/teacher_holdout.jsonl")
ap.add_argument("--earlier", default="data/optimized_4h_v22/earlier_val.jsonl")
ap.add_argument("--ads", default="data/raw/gmeow/server_export_20261031/ads_final.jsonl")
ap.add_argument("--val_texts", default="data/cleaned/gmeow/val_split_texts.jsonl")
ap.add_argument("--out", default="outputs/v22_vs_v21_eval.json")
args = ap.parse_args()
sets = {
"teacher_holdout": load(args.teacher),
"earlier_val": load(args.earlier),
"gmeow_val128": gmeow_val_rows(args.ads, args.val_texts),
}
print(f"gmeow_val128 n={len(sets['gmeow_val128'])} "
f"kind={sum(1 for r in sets['gmeow_val128'] if r['label_kind']>=0)} "
f"sev={sum(1 for r in sets['gmeow_val128'] if r['label_sev']>=0)}")
results = {}
for tag, d in (("v2.2", args.v22), ("v2.1", args.v21)):
print(f"== {tag} ({d}) ==", flush=True)
results[tag] = eval_model(d, sets)
out = Path(args.out)
out.write_text(json.dumps(results, ensure_ascii=False, indent=2), encoding="utf-8")
print("->", out.resolve())
if __name__ == "__main__":
main()