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
Download inference.py from PabloRod11/occupation-hitl-classifier: direct link, hf CLI and curl.
- Browser
- Download file 5.77 kB
-
https://huggingface.co/PabloRod11/occupation-hitl-classifier/resolve/main/inference.py
- Command line
-
hf download hf://PabloRod11/occupation-hitl-classifier/inference.py
-
curl -L -o inference.py https://huggingface.co/PabloRod11/occupation-hitl-classifier/resolve/main/inference.py
5.77 kB
| """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)) | |