Instructions to use echoforest/market-psych-classifier-v1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use echoforest/market-psych-classifier-v1 with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 4,593 Bytes
3d7c238 | 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 | """Per-field temperature scaling on stored logits (pure Python). Run as ``python -m classifier.calibrate``.
Prediction rows (written by ``predict.py``) hold raw logits per field in taxonomy option order:
``{"id", "split", "n_tokens", "truncated", "logits": {field: [float, ...]}}``. Calibrated probabilities are
softmax(logits / T_field). Temperatures are fitted by NLL grid search on dev rows with a non-null target;
``crossfit`` fits them on each of two author-group halves of dev and scores the other half, which gives an
out-of-sample calibration check without touching the benchmark.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import math
from pathlib import Path
from .taxonomy import Taxonomy, 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, 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(row: dict, tax: Taxonomy, temps: dict | None = None) -> dict[str, dict[str, float]]:
temps = temps or {}
return {f: dict(zip(tax.fields[f], softmax(row['logits'][f], temps.get(f, 1.0)))) for f in tax.names}
def gated(row: dict, tax: Taxonomy, temps: dict | None = None) -> dict[str, dict[str, float]]:
return gate(probs(row, tax, temps))
def nll(pairs, tax: Taxonomy, field: str, t: float) -> float | None:
"""Mean negative log-likelihood of the gold option; pairs are (prediction row, targets dict)."""
total, n = 0.0, 0
options = tax.fields[field]
for row, targets in pairs:
gold = targets.get(field)
if gold is None:
continue
p = softmax(row['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]:
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_id: str, seed: str = 'classifier-crossfit') -> int:
return int(hashlib.sha256(json.dumps([seed, author_id]).encode()).hexdigest()[:8], 16) % 2
def crossfit(pairs_with_author, tax: Taxonomy) -> dict:
"""Temperatures fitted on one author half and applied to the other: (row, targets, author) triples.
Returns the two fitted temperature sets and the out-of-half calibrated rows (id -> calibrated probs)."""
halves = {0: [], 1: []}
for row, targets, author in pairs_with_author:
halves[author_half(author)].append((row, targets))
temps = {h: fit(halves[h], tax) for h in (0, 1)}
out = {}
for h in (0, 1):
for row, _ in halves[h]:
out[row['id']] = probs(row, tax, temps[1 - h])
return {'temperatures_by_half': {str(h): temps[h] for h in (0, 1)}, 'calibrated': out,
'sizes': {str(h): len(halves[h]) for h in (0, 1)}}
def load_jsonl(path) -> list[dict]:
with open(path, encoding='utf-8') as fh:
return [json.loads(line) for line in fh if line.strip()]
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
sub = parser.add_subparsers(dest='command', required=True)
f = sub.add_parser('fit', help='Fit temperatures on dev predictions')
f.add_argument('predictions')
f.add_argument('--data', required=True, help='Export directory (taxonomy.json, dev.jsonl)')
f.add_argument('--out', required=True, help='temperatures.json')
args = parser.parse_args(argv)
if Path(args.out).exists():
raise SystemExit('Refusing to overwrite ' + args.out)
tax = Taxonomy.load(args.data)
gold = {r['id']: r['targets'] for r in load_jsonl(Path(args.data) / 'dev.jsonl')}
preds = load_jsonl(args.predictions)
ids = [p['id'] for p in preds]
if len(set(ids)) != len(ids) or set(ids) != set(gold):
raise SystemExit('Predictions must cover each of the %d dev rows exactly once (got %d rows, %d distinct)'
% (len(gold), len(ids), len(set(ids))))
temps = fit([(p, gold[p['id']]) for p in preds], tax)
edge = sorted(f for f, 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())
|