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/build_optimized_dataset.py from x6nux/laya-ad: direct link, hf CLI and curl.
- Browser
- Download file 8.27 kB
-
https://huggingface.co/x6nux/laya-ad/resolve/main/code/scripts/build_optimized_dataset.py
- Command line
-
hf download hf://x6nux/laya-ad/code/scripts/build_optimized_dataset.py
-
curl -L -o build_optimized_dataset.py https://huggingface.co/x6nux/laya-ad/resolve/main/code/scripts/build_optimized_dataset.py
8.27 kB
| #!/usr/bin/env python3 | |
| """构建优化训练集:teacher 清洗数据 + 之前 25k 数据合并,广告过采样 + 稀有 kind/severity 补齐。 | |
| - teacher 数据优先(同 text),早前数据补充;排除外部测试集文本,避免泄漏 | |
| - --gmeow 接入 gmeow 封禁广告语料(ads_final.jsonl,text/label/kind/scope/severity), | |
| --gmeow_val_texts 指定留出 val 文本清单:留出行只进 val(并从 teacher/earlier 中剔除同文本防泄漏) | |
| - normal 的 scope/severity 置 -100(交给二分类 gate),scope/severity 头只学广告 | |
| - train 广告过采样到 20%;kind 每类最少 1000;severity 0/1 最少 500/1000 | |
| - 重复样本用 marker_augment 生成标记形态变体,降低死记 | |
| - 输出 data/optimized_4h/{train,dev,val,holdout827}.jsonl + teacher_holdout/earlier_val | |
| """ | |
| import argparse | |
| import json | |
| import random | |
| from collections import Counter | |
| from pathlib import Path | |
| import numpy as np | |
| from sklearn.model_selection import train_test_split | |
| from marker_augment import augment_markers | |
| 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 key(text: str) -> str: | |
| return "".join(text.split()).lower() | |
| def from_gmeow(r): | |
| sev = r.get("severity") | |
| return { | |
| "text": r["text"], | |
| "label_bin": 0, | |
| "label_kind": KINDS.index(r["kind"]) if r.get("kind") in KINDS else -100, | |
| "label_scope": (0 if r.get("scope") == "account" else 1) if r.get("scope") in ("account", "message") else -100, | |
| "label_sev": int(round(sev)) if isinstance(sev, (int, float)) and 0 <= sev <= 3 else -100, | |
| "source": "gmeow", | |
| } | |
| def from_teacher(r): | |
| is_ad = r.get("label") == "ad" | |
| kind = r.get("ad_kind") | |
| return { | |
| "text": r["text"], | |
| "label_bin": 0 if is_ad else 1, | |
| "label_kind": KINDS.index(kind) if (is_ad and kind in KINDS) else -100, | |
| "label_scope": (0 if r.get("scope") == "account" else 1) if is_ad else -100, | |
| "label_sev": (int(r.get("severity_bucket", -100))) if is_ad else -100, | |
| "source": "teacher", | |
| } | |
| def from_earlier(r): | |
| is_ad = r["label_bin"] == 0 | |
| return { | |
| "text": r["text"], | |
| "label_bin": r["label_bin"], | |
| "label_kind": r["label_kind"] if is_ad else -100, | |
| "label_scope": r["label_scope"] if is_ad else -100, | |
| "label_sev": r["label_sev"] if is_ad else -100, | |
| "source": "earlier", | |
| } | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--teacher", default="data/cleaned/archive_latest_full_clean.jsonl") | |
| ap.add_argument("--earlier_train", default="data/processed_4h/train.jsonl") | |
| ap.add_argument("--external_teacher", default="data/processed_teacher/holdout827.jsonl") | |
| ap.add_argument("--external_earlier", default="data/processed_4h/val.jsonl") | |
| ap.add_argument("--out", default="data/optimized_4h") | |
| ap.add_argument("--seed", type=int, default=42) | |
| ap.add_argument("--ad_ratio", type=float, default=0.20) | |
| ap.add_argument("--kind_min", type=int, default=1000) | |
| ap.add_argument("--sev0_min", type=int, default=500) | |
| ap.add_argument("--sev1_min", type=int, default=1000) | |
| ap.add_argument("--gmeow", default=None, | |
| help="gmeow ads_final.jsonl(text/label/kind/scope/severity)") | |
| ap.add_argument("--gmeow_val_texts", default=None, | |
| help="jsonl({text}):gmeow 留出 val 的文本清单") | |
| args = ap.parse_args() | |
| teacher = load(args.teacher) | |
| earlier = load(args.earlier_train) | |
| gmeow_hold, gmeow_in = [], [] | |
| if args.gmeow: | |
| hold_keys = set() | |
| if args.gmeow_val_texts: | |
| hold_keys = {key(r["text"]) for r in load(args.gmeow_val_texts)} | |
| gmeow = [from_gmeow(r) for r in load(args.gmeow) | |
| if r.get("label") == "ad" and r.get("kind") in KINDS] | |
| gmeow_hold = [m for m in gmeow if key(m["text"]) in hold_keys] | |
| gmeow_in = [m for m in gmeow if key(m["text"]) not in hold_keys] | |
| print(f"gmeow ads={len(gmeow)} train_pool={len(gmeow_in)} val_hold={len(gmeow_hold)}") | |
| ext_t = [from_earlier(r) if "label_bin" in r else from_teacher(r) for r in load(args.external_teacher)] | |
| ext_e = [from_earlier(r) if "label_bin" in r else from_teacher(r) for r in load(args.external_earlier)] | |
| ext_texts = {r["text"] for r in ext_t + ext_e} | |
| hold_keys = {key(m["text"]) for m in gmeow_hold} | |
| combined = {} | |
| for m in gmeow_in: | |
| if m["text"] not in ext_texts: | |
| combined[m["text"]] = m | |
| for r in earlier: | |
| m = from_earlier(r) | |
| if m["text"] not in ext_texts and key(m["text"]) not in hold_keys: | |
| combined[m["text"]] = m | |
| for r in teacher: | |
| m = from_teacher(r) | |
| if m["text"] not in ext_texts and key(m["text"]) not in hold_keys: | |
| combined[m["text"]] = m | |
| rows = list(combined.values()) | |
| print("combined unique", len(rows), "ads", sum(1 for r in rows if r["label_bin"] == 0), | |
| "kind", Counter(r["label_kind"] for r in rows if r["label_kind"] >= 0), | |
| "sev", Counter(r["label_sev"] for r in rows if r["label_sev"] >= 0)) | |
| y = np.array([r["label_bin"] for r in rows]) | |
| idx = np.arange(len(rows)) | |
| tr, rest = train_test_split(idx, test_size=0.10, stratify=y, random_state=args.seed) | |
| dev, rest2 = train_test_split(rest, test_size=0.50, stratify=y[rest], random_state=args.seed) | |
| val, hold = train_test_split(rest2, test_size=0.50, stratify=y[rest2], random_state=args.seed) | |
| splits = {"train": [rows[i] for i in tr], "dev": [rows[i] for i in dev], | |
| "val": [rows[i] for i in val], "holdout827": [rows[i] for i in hold]} | |
| splits["val"] = splits["val"] + gmeow_hold | |
| if gmeow_hold: | |
| print("gmeow val holdout", len(gmeow_hold), | |
| "kind", Counter(KINDS[m["label_kind"]] for m in gmeow_hold if m["label_kind"] >= 0)) | |
| train = splits["train"] | |
| rng = random.Random(args.seed) | |
| normals = sum(1 for r in train if r["label_bin"] == 1) | |
| ads = [r for r in train if r["label_bin"] == 0] | |
| target_ads = int(args.ad_ratio / (1 - args.ad_ratio) * normals) | |
| extra = max(0, target_ads - len(ads)) | |
| def dup(src, n): | |
| out = [] | |
| for i in range(n): | |
| r = dict(rng.choice(src)) | |
| r["text"] = augment_markers(r["text"], random.Random(args.seed * 1000 + i), p=0.5) | |
| r["source"] = "oversample" | |
| out.append(r) | |
| return out | |
| train += dup(ads, extra) | |
| for k in range(len(KINDS)): | |
| pool = [r for r in train if r["label_kind"] == k] | |
| if pool and len(pool) < args.kind_min: | |
| train += dup(pool, args.kind_min - len(pool)) | |
| for sev, minimum in ((0, args.sev0_min), (1, args.sev1_min)): | |
| pool = [r for r in train if r["label_sev"] == sev] | |
| if pool and len(pool) < minimum: | |
| train += dup(pool, minimum - len(pool)) | |
| splits["train"] = train | |
| out = Path(args.out) | |
| out.mkdir(parents=True, exist_ok=True) | |
| for name, data in splits.items(): | |
| with open(out / f"{name}.jsonl", "w", encoding="utf-8") as f: | |
| for x in data: | |
| f.write(json.dumps(x, ensure_ascii=False) + "\n") | |
| print(name, len(data), "bin", Counter(x["label_bin"] for x in data), | |
| "kind", Counter(x["label_kind"] for x in data if x["label_kind"] >= 0), | |
| "scope", Counter(x["label_scope"] for x in data if x["label_scope"] >= 0), | |
| "sev", Counter(x["label_sev"] for x in data if x["label_sev"] >= 0)) | |
| for name, data in (("teacher_holdout", ext_t), ("earlier_val", ext_e)): | |
| with open(out / f"{name}.jsonl", "w", encoding="utf-8") as f: | |
| for x in data: | |
| f.write(json.dumps(x, ensure_ascii=False) + "\n") | |
| print(name, len(data), "bin", Counter(x["label_bin"] for x in data), | |
| "kind", Counter(x["label_kind"] for x in data if x["label_kind"] >= 0)) | |
| print("out", out.resolve()) | |
| if __name__ == "__main__": | |
| main() | |