Instructions to use echoforest/naver-psy-classifier-gemma4-12b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use echoforest/naver-psy-classifier-gemma4-12b with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 6,185 Bytes
210f9af | 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """Per-field temperature scaling on stored logits (pure Python). Run as ``python -m training.calibrate``.
Prediction rows (``training.predict``, the pod, and the node12 server) hold raw logits per field in taxonomy option
order: ``{"id", "split", "n_tokens", "truncated", "logits": {field: [float, ...]}}``. Calibrated probabilities are
softmax(logits / T_field); the served/stored probabilities are those, gated (``taxonomy.gate``). One temperature
per field is fitted on val by NLL (grid search) over the rows whose target for that field is not masked, and frozen
before test (classifier-plan §5.3). ``crossfit`` fits on each of two author halves of val and scores the other half.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import math
from pathlib import Path
from typing import Iterable, Mapping
from .taxonomy import Taxonomy, argmax, gate
T_GRID = tuple(round(0.1 * (100 ** (i / 160)), 4) for i in range(161)) # 0.1 .. 10, geometric
FLOOR = 1e-12
def softmax(logits: Iterable[float], t: float = 1.0) -> list[float]:
scaled = [x / t for x in logits]
m = max(scaled)
exp = [math.exp(x - m) for x in scaled]
total = sum(exp)
return [e / total for e in exp]
def probs(logits: Mapping[str, list[float]], tax: Taxonomy, temps: Mapping[str, float] | None = None
) -> dict[str, dict[str, float]]:
"""Calibrated (ungated) distributions from per-field logits."""
temps = temps or {}
return {f: dict(zip(tax.fields[f], softmax(logits[f], temps.get(f, 1.0)))) for f in tax.names}
def gated(logits: Mapping[str, list[float]], tax: Taxonomy, temps: Mapping[str, float] | None = None
) -> dict[str, dict[str, float]]:
"""Calibrated and gated distributions: what the server returns and ``post_predictions.probs`` stores."""
return gate(probs(logits, tax, temps))
def outputs(logits: Mapping[str, list[float]], tax: Taxonomy, temps: Mapping[str, float] | None = None) -> dict:
"""Everything a prediction carries besides the logits: gated probabilities and gated argmax labels."""
p = gated(logits, tax, temps)
return {"probs": p, "labels": argmax(p)}
def max_prob_diff(a: Mapping[str, Mapping[str, float]], b: Mapping[str, Mapping[str, float]]) -> float:
"""Largest absolute difference between two {field: {option: p}} sets (the same fields and options)."""
if set(a) != set(b):
raise ValueError("field sets differ")
out = 0.0
for f in a:
if set(a[f]) != set(b[f]):
raise ValueError(f"option sets of {f} differ")
out = max(out, max(abs(a[f][o] - b[f][o]) for o in a[f]))
return out
def _gold(targets: Mapping[str, str | None], mask: Iterable[str], field: str) -> str | None:
return None if field in set(mask) else targets.get(field)
def nll(pairs, tax: Taxonomy, field: str, t: float) -> float | None:
"""Mean NLL of the target option; pairs are (prediction row, export row with targets and mask)."""
total, n = 0.0, 0
options = tax.fields[field]
for pred, row in pairs:
gold = _gold(row["targets"], row.get("mask", ()), field)
if gold is None:
continue
p = softmax(pred["logits"][field], t)[options.index(gold)]
total -= math.log(max(p, FLOOR))
n += 1
return total / n if n else None
def fit(pairs, tax: Taxonomy) -> dict[str, float]:
pairs = list(pairs)
out = {}
for f in tax.names:
if nll(pairs, tax, f, 1.0) is None:
out[f] = 1.0
continue
out[f] = min(T_GRID, key=lambda t: nll(pairs, tax, f, t))
return out
def author_half(author_key: str, seed: str = "nsb-crossfit") -> int:
return int(hashlib.sha256(json.dumps([seed, author_key]).encode()).hexdigest()[:8], 16) % 2
def crossfit(pairs, tax: Taxonomy) -> dict:
"""Temperatures fitted on one author half of val and applied to the other.
Returns both temperature sets and, per row id, the out-of-half calibrated (ungated) distributions."""
halves: dict[int, list] = {0: [], 1: []}
for pred, row in pairs:
halves[author_half(row["author_key"])].append((pred, row))
temps = {h: fit(halves[h], tax) for h in (0, 1)}
calibrated = {}
for h in (0, 1):
for pred, _ in halves[h]:
calibrated[pred["id"]] = probs(pred["logits"], tax, temps[1 - h])
return {"temperatures_by_half": {str(h): temps[h] for h in (0, 1)}, "calibrated": calibrated,
"sizes": {str(h): len(halves[h]) for h in (0, 1)}}
def pair_up(preds: list[dict], rows: list[dict]) -> list[tuple[dict, dict]]:
"""(prediction, export row) pairs; every row needs exactly one prediction."""
by_id: dict[str, dict] = {}
for p in preds:
if p["id"] in by_id:
raise ValueError(f"duplicate prediction {p['id']}")
by_id[p["id"]] = p
missing = [r["id"] for r in rows if r["id"] not in by_id]
if missing:
raise ValueError(f"{len(missing)} rows have no prediction (first: {missing[0]})")
return [(by_id[r["id"]], r) for r in rows]
def main(argv=None) -> int:
from .data import load_jsonl, load_split
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
sub = parser.add_subparsers(dest="command", required=True)
f = sub.add_parser("fit", help="Fit temperatures on val predictions")
f.add_argument("predictions")
f.add_argument("--data", required=True, help="Bundle directory (taxonomy.json, val.jsonl)")
f.add_argument("--out", required=True, help="temperatures.json")
f.add_argument("--split", default="val")
args = parser.parse_args(argv)
if Path(args.out).exists():
raise SystemExit("refusing to overwrite " + args.out)
tax = Taxonomy.load(args.data)
rows = load_split(args.data, args.split)
pairs = pair_up(load_jsonl(args.predictions), rows)
temps = fit(pairs, tax)
edge = sorted(k for k, t in temps.items() if t in (T_GRID[0], T_GRID[-1]))
Path(args.out).write_text(json.dumps(temps, indent=1) + "\n")
print(json.dumps({"temperatures": temps, "at_grid_edge": edge}))
return 0
if __name__ == "__main__":
raise SystemExit(main())
|