File size: 3,022 Bytes
cfd7c14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""Load the six family classifiers and predict the family of a reasoning trace.

IMPORTANT: the vectorizers were pickled holding a reference to
`preprocessing.simple_tokenizer`, so `preprocessing.py` (shipped in this repo)
must be importable or `joblib.load` raises
`ModuleNotFoundError: No module named 'preprocessing'`.

    python load_classifiers.py            # runs the built-in demo
"""

import os
import sys

import joblib
import numpy as np

FAMILIES = ["openai", "qwen", "deepseek", "olmo", "glm", "exaone"]
SUBDIR = "tf_idf"


def load(repo_dir: str = "."):
    """Return (classifiers, vectorizers, preprocess_text) keyed by family."""
    if repo_dir not in sys.path:
        sys.path.insert(0, repo_dir)          # so `preprocessing` resolves
    from preprocessing import preprocess_text

    base = os.path.join(repo_dir, SUBDIR)
    clfs = {f: joblib.load(os.path.join(base, f"classifier_{f}.joblib"))
            for f in FAMILIES}
    vecs = {f: joblib.load(os.path.join(base, f"vectorizer_{f}.joblib"))
            for f in FAMILIES}
    return clfs, vecs, preprocess_text


def predict(texts, clfs, vecs, preprocess_text):
    """Family probabilities and argmax prediction for a list of raw traces.

    Each classifier is an independent one-vs-rest model, so the six probabilities
    do NOT sum to 1. `argmax` is a reasonable combination rule but not a
    calibrated posterior.
    """
    pre = [preprocess_text(t) for t in texts]
    probs = np.zeros((len(pre), len(FAMILIES)))
    for j, fam in enumerate(FAMILIES):
        probs[:, j] = clfs[fam].predict_proba(vecs[fam].transform(pre))[:, 1]
    pred = np.asarray(FAMILIES)[probs.argmax(axis=1)]
    return pred, probs


def download(repo_id: str = "SupritiVijay/classifiers_model_provenance") -> str:
    """Grab the repo locally and return the path."""
    from huggingface_hub import snapshot_download
    return snapshot_download(repo_id)


def main() -> int:
    repo_dir = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(
        os.path.abspath(__file__))
    print(f"loading from {repo_dir}")
    clfs, vecs, preprocess_text = load(repo_dir)

    demo = [
        "<think>\nWe need to compute the expected value. Let me define dp[i] as "
        "the answer for the first i elements. Then dp[i] = dp[i-1] + a[i].\n"
        "</think>\nHere is the solution:\n```python\ndef solve(a):\n"
        "    return sum(a)\n```",
        "<think>\nOkay, so the user wants a function that reverses a string. "
        "That's straightforward. I'll use slicing.\n</think>\n"
        "```python\ndef rev(s):\n    return s[::-1]\n```",
    ]
    pred, probs = predict(demo, clfs, vecs, preprocess_text)

    print(f"\n{'':4s} " + " ".join(f"{f:>9s}" for f in FAMILIES) + "   -> pred")
    for i in range(len(demo)):
        row = " ".join(f"{p:9.3f}" for p in probs[i])
        print(f"  {i}  {row}   -> {pred[i]}")
    print("\n(one-vs-rest probabilities; they do not sum to 1)")
    return 0


if __name__ == "__main__":
    sys.exit(main())