Text Classification
Scikit-learn
English
occupation-classification
human-in-the-loop
business-systems-analysis
tfidf
logistic-regression
onet
Instructions to use PabloRod11/occupation-hitl-classifier with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Scikit-learn
How to use PabloRod11/occupation-hitl-classifier with Scikit-learn:
from huggingface_hub import hf_hub_download import joblib model = joblib.load( hf_hub_download("PabloRod11/occupation-hitl-classifier", "sklearn_model.joblib") ) # only load pickle files from sources you trust # read more about it here https://skops.readthedocs.io/en/stable/persistence.html - Notebooks
- Google Colab
- Kaggle
File size: 5,769 Bytes
a8b5f43 | 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 | """Load the safe JSON baseline. Inference needs only Python's standard library."""
import json
import math
from pathlib import Path
import re
from collections import Counter
TOKEN_PATTERN = r"(?u)\b\w\w+\b"
def route_confidence(confidence, thresholds, known_features=True):
"""Model routing policy; absence of known text always requires escalation."""
if isinstance(confidence, bool) or not isinstance(confidence, (int, float)) or not math.isfinite(confidence) or not 0 <= confidence <= 1:
raise ValueError('Confidence must be a finite probability')
auto = thresholds['auto_accept']
return ('ESCALATE' if not known_features or confidence < thresholds['escalate']
else 'AUTO_ACCEPT' if auto is not None and confidence >= auto else 'HUMAN_REVIEW')
def validate_model(model):
"""Fail at load time rather than emit plausible predictions from corrupt data."""
try:
if type(model['format_version']) is not int or model['format_version'] != 1:
raise ValueError('Unsupported model format')
expected = {'token_pattern': TOKEN_PATTERN, 'ngram_range': [1, 2],
'lowercase': True, 'sublinear_tf': True, 'norm': 'l2'}
if model['vectorizer'] != expected:
raise ValueError('Unsupported vectorizer configuration')
classes, vocabulary = model['classes'], model['vocabulary']
if not isinstance(classes, list) or len(classes) != 5 or any(not isinstance(c, str) or not c for c in classes) or len(set(classes)) != 5:
raise ValueError('Expected five unique classes')
if not isinstance(model['model_version'], str) or not model['model_version'].strip():
raise ValueError('Missing model version')
if not isinstance(vocabulary, dict) or not vocabulary or any(not isinstance(k, str) or type(v) is not int for k, v in vocabulary.items()) or set(vocabulary.values()) != set(range(len(vocabulary))):
raise ValueError('Invalid vocabulary indices')
width = len(vocabulary)
def numbers(values, size):
return isinstance(values, list) and len(values) == size and all(type(v) in (int, float) and math.isfinite(v) for v in values)
if not numbers(model['idf'], width) or any(v <= 0 for v in model['idf']):
raise ValueError('Invalid IDF values')
if not numbers(model['intercept'], 5) or not isinstance(model['coefficients'], list) or len(model['coefficients']) != 5 or any(not numbers(row, width) for row in model['coefficients']):
raise ValueError('Invalid classifier dimensions or weights')
thresholds = model['thresholds']
if not isinstance(thresholds, dict) or set(thresholds) != {'auto_accept', 'escalate'}:
raise ValueError('Invalid threshold configuration')
for value in thresholds.values():
if value is not None and (type(value) not in (int, float) or not math.isfinite(value) or not 0 <= value <= 1):
raise ValueError('Invalid routing threshold')
if thresholds['escalate'] is None or (thresholds['auto_accept'] is not None and thresholds['auto_accept'] < thresholds['escalate']):
raise ValueError('Inconsistent routing thresholds')
except (KeyError, TypeError) as exc:
raise ValueError('Incomplete or malformed model artifact') from exc
class OccupationClassifier:
def __init__(self, path=None):
directory = Path(__file__).parent
self.path = Path(path) if path is not None else (directory / 'model.json' if (directory / 'model.json').exists() else directory / 'model' / 'model.json')
self.model = json.loads(self.path.read_text())
validate_model(self.model)
self.classes = self.model['classes']
self.thresholds = self.model['thresholds']
def predict(self, text):
if not isinstance(text, str) or not text.strip():
raise ValueError('Enter a nonempty job title or description.')
if len(text) > 10000:
raise ValueError('Text must be at most 10,000 characters.')
tokens = re.findall(TOKEN_PATTERN, text.lower())
terms = tokens + [' '.join(pair) for pair in zip(tokens, tokens[1:])]
counts = Counter(terms)
vocabulary = self.model['vocabulary']
features = {vocabulary[t]: (1 + math.log(n)) * self.model['idf'][vocabulary[t]]
for t, n in counts.items() if t in vocabulary}
norm = math.sqrt(sum(value * value for value in features.values()))
if norm:
features = {index: value / norm for index, value in features.items()}
logits = [bias + sum(weights[index] * value for index, value in features.items())
for bias, weights in zip(self.model['intercept'], self.model['coefficients'])]
maximum = max(logits)
exp = [math.exp(value - maximum) for value in logits]
probabilities = [value / sum(exp) for value in exp]
winner = max(range(len(probabilities)), key=probabilities.__getitem__)
confidence = probabilities[winner]
route = route_confidence(confidence, self.thresholds, bool(features))
return {'label': self.classes[winner], 'prediction': self.classes[winner], 'confidence': confidence, 'route': route,
'probabilities': dict(zip(self.classes, probabilities)),
'known_features': len(features), 'model_version': self.model['model_version']}
Classifier = OccupationClassifier
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('text')
parser.add_argument('--model', type=Path)
args = parser.parse_args()
print(json.dumps(OccupationClassifier(args.model).predict(args.text), indent=2))
|