Text Classification
Transformers
Safetensors
Chinese
spam-detection
advertising-detection
ad-classification
chinese
modernbert
mmbert
multi-task
four-head
Instructions to use x6nux/laya-ad with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use x6nux/laya-ad with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="x6nux/laya-ad")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("x6nux/laya-ad", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download code/scripts/predict_dual_head.py from x6nux/laya-ad: direct link, hf CLI and curl.
- Browser
- Download file 4.08 kB
-
https://huggingface.co/x6nux/laya-ad/resolve/main/code/scripts/predict_dual_head.py
- Command line
-
hf download hf://x6nux/laya-ad/code/scripts/predict_dual_head.py
-
curl -L -o predict_dual_head.py https://huggingface.co/x6nux/laya-ad/resolve/main/code/scripts/predict_dual_head.py
4.08 kB
| #!/usr/bin/env python3 | |
| """四头模型推理:一次前向输出 is_ad / kind / scope / severity(0–3 期望分数)。""" | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| import torch | |
| from transformers import AutoTokenizer | |
| from dual_head_model import load_dual_head_any | |
| KINDS = ["crypto", "gambling", "porn", "porn_bait", "promo", "scam", "spam_flood"] | |
| DEMO = [ | |
| "加微信 abc123 免费领取涨粉秘籍,名额有限!", | |
| "今晚合唱团排练改到七点半,别走错了", | |
| "这款面霜绝了,用了一个月肉眼可见变好,链接放评论区", | |
| ] | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--model", default="outputs/ad-4head-teacher-heads/best") | |
| ap.add_argument("--text", default=None) | |
| ap.add_argument("--file", default=None, help="jsonl({text}) 或 txt,每行一条") | |
| ap.add_argument("--threshold", type=float, default=0.5) | |
| ap.add_argument("--kind_threshold", type=float, default=0.0, | |
| help=">0 时 kind 置信度低于该值输出 ad_uncertain") | |
| ap.add_argument("--batch_size", type=int, default=64) | |
| ap.add_argument("--max_len", type=int, default=384) | |
| args = ap.parse_args() | |
| if args.text: | |
| texts = [args.text] | |
| elif args.file: | |
| if args.file.endswith(".jsonl"): | |
| texts = [json.loads(l)["text"] for l in open(args.file, encoding="utf-8") if l.strip()] | |
| else: | |
| texts = [l.rstrip("\n") for l in open(args.file, encoding="utf-8") if l.strip()] | |
| else: | |
| texts = DEMO | |
| cfg_path = Path(args.model) / "dual_config.json" | |
| dc = json.loads(cfg_path.read_text(encoding="utf-8")) if cfg_path.is_file() else {} | |
| kind_labels = dc.get("kind_labels") or KINDS | |
| scope_labels = dc.get("scope_labels") or [] | |
| severity_labels = dc.get("severity_labels") or [] | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| model = load_dual_head_any(args.model, device=device, dtype=torch.bfloat16) | |
| tok = AutoTokenizer.from_pretrained(args.model) | |
| with torch.inference_mode(): | |
| for i in range(0, len(texts), args.batch_size): | |
| chunk = texts[i:i + args.batch_size] | |
| enc = tok(chunk, truncation=True, max_length=args.max_len, | |
| padding=True, return_tensors="pt").to(device) | |
| with torch.autocast(device_type=device, dtype=torch.bfloat16, enabled=device == "cuda"): | |
| blog, klog, slog, vlog, _ = model.forward_all(enc["input_ids"], enc["attention_mask"]) | |
| p_ad = torch.softmax(blog.float(), -1)[:, 0].cpu() | |
| pk = torch.softmax(klog.float(), -1).cpu() | |
| ps = torch.softmax(slog.float(), -1).cpu() if slog is not None else None | |
| pv = torch.softmax(vlog.float(), -1).cpu() if vlog is not None else None | |
| for j, (t, pa) in enumerate(zip(chunk, p_ad)): | |
| is_ad = bool(pa >= args.threshold) | |
| k = int(pk[j].argmax()) | |
| kind = kind_labels[k] if is_ad else "normal" | |
| if is_ad and args.kind_threshold > 0 and float(pk[j][k]) < args.kind_threshold: | |
| kind = "ad_uncertain" | |
| out = {"is_ad": is_ad, "p_ad": round(float(pa), 6), | |
| "kind": kind, "kind_conf": round(float(pk[j][k]), 6), | |
| "kind_probs": {n: round(float(pk[j][x]), 6) for x, n in enumerate(kind_labels)}} | |
| if is_ad and ps is not None: | |
| s = int(ps[j].argmax()) | |
| out["scope"] = scope_labels[s] if scope_labels else str(s) | |
| out["scope_probs"] = {n: round(float(ps[j][x]), 6) for x, n in enumerate(scope_labels)} | |
| if is_ad and pv is not None: | |
| probs = [float(x) for x in pv[j]] | |
| out["severity_score"] = round(sum(x * p for x, p in enumerate(probs)), 4) | |
| out["severity_probs"] = {n: round(probs[x], 6) for x, n in enumerate(severity_labels)} | |
| print(json.dumps({"text": t, **out}, ensure_ascii=False)) | |
| if __name__ == "__main__": | |
| main() | |