Download scripts/train_baseline.py from chaliasif/saudi-tender-tagger: direct link, hf CLI and curl.
- Browser
- Download file 2.87 kB
-
https://huggingface.co/chaliasif/saudi-tender-tagger/resolve/main/scripts/train_baseline.py
- Command line
-
hf download hf://chaliasif/saudi-tender-tagger/scripts/train_baseline.py
-
curl -L -o train_baseline.py https://huggingface.co/chaliasif/saudi-tender-tagger/resolve/main/scripts/train_baseline.py
2.87 kB
| #!/usr/bin/env python3 | |
| """TF-IDF (word 1-2 + char_wb 2-5) + logistic regression, one model per label. Usage: train_baseline.py""" | |
| import json, os, sys, time, joblib | |
| import numpy as np, pandas as pd | |
| from scipy.sparse import hstack | |
| from sklearn.feature_extraction.text import TfidfVectorizer | |
| from sklearn.linear_model import LogisticRegression | |
| from sklearn.metrics import accuracy_score, f1_score | |
| from sklearn.model_selection import train_test_split | |
| sys.path.insert(0, os.path.dirname(__file__)); from common import * | |
| tr = pd.read_csv(f"{ROOT}/data/published/train.csv", dtype=str).fillna(""); te = pd.read_csv(f"{ROOT}/data/published/test.csv", dtype=str).fillna("") | |
| ttl = lambda d: [make_input(t, a) for t, a in zip(d.title, d.agency)] | |
| def ttype_label(d, ref): # same collapse as activity: map unseen/rare to other using train counts | |
| return d.ttype | |
| LABELS = {"activity": "activity_label", "ttype": "ttype", "sector": "sector", "kind": "kind"} | |
| # collapse rare ttype exactly as in make_splits (<8 overall -> other); recompute from stats | |
| stats = json.load(open(f"{ROOT}/data/stats.json")); keep_t = {k for k, v in stats["ttype_counts"] if k != "other"} | |
| tr["ttype"] = tr.ttype.map(lambda x: x if x in keep_t else "other"); te["ttype"] = te.ttype.map(lambda x: x if x in keep_t else "other") | |
| res = {} | |
| os.makedirs(f"{ROOT}/outputs", exist_ok=True) | |
| for name, col in LABELS.items(): | |
| t0 = time.time() | |
| a, b = train_test_split(tr, test_size=0.1, random_state=SEED, stratify=tr[col] if tr[col].value_counts().min() >= 2 else None) | |
| def fit(df, C): | |
| wv = TfidfVectorizer(analyzer="word", ngram_range=(1, 2), min_df=1, sublinear_tf=True) | |
| cv = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 5), min_df=2, sublinear_tf=True) | |
| X = hstack([wv.fit_transform(ttl(df)), cv.fit_transform(ttl(df))]).tocsr() | |
| m = LogisticRegression(C=C, max_iter=2000, solver="lbfgs").fit(X, df[col]); return wv, cv, m | |
| def pred(p, df): wv, cv, m = p; return m.predict(hstack([wv.transform(ttl(df)), cv.transform(ttl(df))]).tocsr()) | |
| best = max([1, 10, 50], key=lambda C: f1_score(b[col], pred(fit(a, C), b), average="macro")) | |
| p = fit(tr, best); yp = pred(p, te) | |
| res[name] = {"C": best, "accuracy": accuracy_score(te[col], yp), "macro_f1": f1_score(te[col], yp, average="macro"), | |
| "seconds": round(time.time() - t0, 1), "n_classes": int(tr[col].nunique())} | |
| pd.DataFrame({"ref": te.ref, "true": te[col], "pred": yp}).to_csv(f"{ROOT}/outputs/pred_baseline_{name}.csv", index=False) | |
| if name in ("activity", "ttype", "sector"): | |
| os.makedirs(f"{ROOT}/models/saudi-tender-tagger/{name}_tfidf", exist_ok=True) | |
| joblib.dump(p, f"{ROOT}/models/saudi-tender-tagger/{name}_tfidf/model.joblib", compress=3) | |
| print(name, res[name], flush=True) | |
| json.dump(res, open(f"{ROOT}/outputs/baseline_metrics.json", "w"), indent=1) | |