Handleidingen
Tutorial: een classifier en een regressor
Een uitvoerbare doorloop voor drie voorspellende modellen die vanaf de commandoregel worden geëvalueerd: een binaire churnclassifier, een ticketrouter met drie klassen die per klasse wordt geëvalueerd, en een regressor voor levertijden die wordt gescoord op absolute fout binnen een gedeclareerd bereik. Elk draait lokaal zonder provider, zonder sleutel en zonder netwerk, en elk eindigt met een kandidaatwijziging die tegen de baseline wordt gemeten.
Deze pagina is de praktische tegenhanger van Classifiers en regressors, die elk veld uitlegt; lees die pagina als referentie en deze om het één keer van begin tot eind te doen. Termen zoals interval, beslissingstoestand en releaseactie worden gedefinieerd in Kernbegrippen.
Wat Oloproof meet voor een voorspellend model, en wat niet
| Model | Wat je krijgt | Wat je niet krijgt |
|---|---|---|
| Binaire classifier | accuracy, recall, precision, Brier-score, log loss, ROC-AUC en average precision, plus confusion-aantallen, een kalibratietabel en een drempelsweep | een aanbevolen drempel |
| Multiclass-classifier | één recall en één precision per klasse, elk een binaire metriek waarvan de positieve klasse die klasse is (één tegen de rest) | een macro- of micro-gemiddelde als metriek |
| Regressor | gemiddelde absolute fout, begrensd door een target_range die je declareert | kwadratische fout, R kwadraat, of een fout zonder gedeclareerd bereik |
Het model zelf blijft van jou. Oloproof roept een Python-functie aan waarnaar je verwijst, leest de voorspelling die ze teruggeeft, en ziet nooit features, gewichten of interne werking.
Vereisten
- Python 3.11 of later en pip install oloproof in een virtuele omgeving, zoals in de quickstart.
- De voorbeeldbestanden, die met het pakket worden meegeleverd. Kopieer ze naar een nieuwe map zodat de stores van de runs daar terechtkomen:
oloproof init --example predictive ~/oloproof-predictive
cd ~/oloproof-predictiveElk commando hieronder draait vanuit een van de drie submappen. Elke run schrijft zijn bewijs naar een map .oloproof/ naast het oloproof.yaml dat hij las.
predictive/
binary/ app.py oloproof.yaml candidate.yaml release.yaml comparison.yaml data/accounts.jsonl
multiclass/ app.py oloproof.yaml candidate.yaml release.yaml data/tickets.jsonl
regression/ app.py oloproof.yaml candidate.yaml release.yaml data/orders.jsonlDeel 1: een binaire classifier
De adapter
binary/app.py bevat het model en de adapter in één bestand. Het model is hetzelfde deterministische churnmodel als in het voorbeeld churn_model (oloproof init --example churn_model); de adapter is de gedecoreerde functie die Oloproof aanroept:
from oloproof import system
@system(name="churn-model", version="baseline")
def run(account):
score = churn_score(account)
return {"label": score >= 0.5, "score": round(score, 4)}
@system(name="churn-model", version="candidate-cutoff-0.4")
def run_candidate(account):
score = churn_score(account)
return {"label": score >= 0.4, "score": round(score, 4)}Om je eigen model te evalueren, houd je de vorm en vervang je churn_score door een aanroep ervan, bijvoorbeeld model.predict_proba([features(account)])[0][1] voor een scikit-learn-model dat je één keer bij het importeren laadt. Wijzig version telkens als het model of zijn afkapwaarde verandert: de versie is deel van de cachesleutel, dus een model dat onder dezelfde versie opnieuw is getraind zou de oude voorspellingen hergebruiken.
Vormen van input en output
Eén regel van data/accounts.jsonl is één case:
{"expected": {"label": false}, "id": "account_000", "input": {"recent_upgrade": true, "support_contacts": 0, "tenure_months": 0}, "metadata": {"plan": "enterprise"}}| Deel | Vorm | Wie het leest |
|---|---|---|
| input | het object dat je functie krijgt | je adapter |
| expected.label | true of false, de waarheid | de evaluators |
| metadata.plan | elke JSON | alleen slices |
| teruggegeven label | true of false, de voorspelling | de evaluators |
| teruggegeven score | een getal in [0, 1], de kans op de positieve klasse | Brier, log loss, rangschikking, kalibratie, sweep |
De evaluators, en waarom deze
binary/oloproof.yaml:
version: 1
project: churn-tutorial
dataset: data/accounts.jsonl
system:
name: churn-model
version: baseline
callable: app:run
evaluators:
- {type: predictive_correct, criterion: accuracy}
- {type: predictive_recall, criterion: recall}
- {type: predictive_precision, criterion: precision}
- {type: predictive_brier, criterion: brier}
- {type: predictive_log_loss, criterion: log_loss, clip: 0.02}
- {type: predictive_ranking, criterion: rank}
metrics:
- {id: roc_auc, type: ranking, criterion: rank, statistic: roc_auc}
- {id: pr_auc, type: ranking, criterion: rank, statistic: average_precision}
predictive:
label_field: label
score_field: score
expected_field: label
positive: true
calibration_bins: 10
thresholds: [0.3, 0.4, 0.5, 0.6, 0.7]
slices: [metadata.plan, "confidence:0.5"]
min_slice_support: 20- Accuracy, recall en precision zijn drie percentages over drie verschillende verzamelingen rijen: elk account, de accounts die vertrokken, en de accounts die het model markeerde. Een churnmodel heeft ze alle drie nodig omdat ongeveer een derde van de accounts vertrekt, dus een model dat voorspelt dat niemand vertrekt is 68% accuraat en vindt niemand.
- Brier en log loss scoren de kans achter het label. Log loss heeft clip nodig, omdat één zelfverzekerde fout anders oneindig is.
- predictive_ranking plus twee metrics:-items geven ROC-AUC en average precision. Ze beantwoorden hoe goed het model accounts ordent, los van waar de afkapwaarde ligt.
- Het predictive:-blok zegt waar het label, de score en de waarheid staan, en produceert de confusion-aantallen, de kalibratietabel en de drempelsweep naast de metrieken.
Het beleid
binary/release.yaml legt een ondergrens op elk percentage:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- {id: accuracy-floor, metric: accuracy, min: 0.75}
- {id: recall-floor, metric: recall, min: 0.60}
- {id: precision-floor, metric: precision, min: 0.60}Een regel slaagt als de ondergrens van het interval de vloer haalt, faalt als de bovengrens eronder ligt, en leest anders INSUFFICIENT_EVIDENCE.
Draai het
cd binary
oloproof runIngekort:
Run run_... [DECIDED/COMPLETE]
Gate: ALLOW (exit 0)
│ accuracy-floor │ accuracy │ PASS │ lower_bound_meets_minimum │
│ recall-floor │ recall │ PASS │ lower_bound_meets_minimum │
│ precision-floor │ precision │ PASS │ lower_bound_meets_minimum │
│ accuracy │ 88.5% │ [83.2%, 92.6%] │ 177 / 200 observed · 0 missing · 0 excluded │
│ recall │ 81.2% │ [69.5%, 90.0%] │ 52 / 64 observed · 0 missing · 136 excluded │
│ precision │ 82.5% │ [70.9%, 91.0%] │ 52 / 63 observed · 0 missing · 137 excluded │
│ brier │ 0.120 │ [0.094, 0.154] │ mean of 200 observed · 0 missing · 0 excluded │
│ log_loss │ 0.389 │ [0.323, 0.499] │ mean of 200 observed · 0 missing · 0 excluded │
│ roc_auc │ 92.3% │ [69.3%, 100.0%] │ roc_auc over 64 positive · 136 negative · 0 missing · 0 excluded │
│ pr_auc │ 86.5% │ │ average_precision over 64 positive · 136 negative · 0 missing · 0 excluded │Zo lees je het:
- Gate: ALLOW (exit 0) betekent dat geen regel een toestand bereikte waarop het beleid blokkeert. Het is geen bewering dat het model goed is buiten de drie vloeren die je schreef.
- De excluded-aantallen zijn de noemers aan het werk: recall wordt gemeten over de 64 accounts die vertrokken, dus de 136 die dat niet deden zijn ervan uitgesloten, niet als mislukkingen geteld.
- pr_auc heeft geen interval. Bij 200 rijen houdt de engine een grens achter die hij niet kan onderbouwen; een regel erop zou INSUFFICIENT_EVIDENCE lezen met interval_unavailable.
Onder de metrieken print dezelfde output de confusion-aantallen, de kalibratietabel en de drempelsweep:
│ actually positive │ 52 │ 12 │
│ actually negative │ 11 │ 125 │
│ 0.2-0.3 │ 26.7% │ 0.0% │ 34 rows │
│ 0.5-0.6 │ 53.4% │ 81.0% │ 21 rows │
│ 0.3 │ 57.4% │ 96.9% │ 62/108 predicted positive · 62/64 actual positive │
│ 0.4 │ 62.6% │ 89.1% │ 57/91 predicted positive · 57/64 actual positive │
│ 0.5 │ 82.5% │ 81.2% │ 52/63 predicted positive · 52/64 actual positive │
│ 0.6 │ 83.3% │ 54.7% │ 35/42 predicted positive · 35/64 actual positive │
│ 0.7 │ 100.0% │ 37.5% │ 24/24 predicted positive · 24/64 actual positive │De aantallen zijn geen percentages en geen regel mag ze noemen. De kalibratierijen zeggen dat de kansen van het model niet kloppen: in de band van 0,2 tot 0,3 beweert het ongeveer één vertrekker op vier en van de 34 vertrok er geen. De sweep heet Thresholds (exploratory; recommends nothing): hij toont wat elke afkapwaarde zou hebben gemeten en laat de keuze aan jou, omdat alleen jij weet wat een gemiste vertrekker kost tegenover een verspild retentiegesprek.
De mislukkingen bekijken
oloproof inspect RUN_ID --failures --limit 523 of 200 cases failed, errored or did not finish
account_032
output: {"label": false, "score": 0.07}
accuracy: failed
recall: failed
account_037
output: {"label": false, "score": 0.37}
accuracy: failed
recall: failed
...RUN_ID is het id op de eerste regel van de output van de run. oloproof inspect RUN_ID --case account_037 toont de input, de verwachte waarde, de output en elk oordeel van één case. Verschillende gemiste vertrekkers zitten net onder de afkapwaarde van 0,5 (0,37, 0,43), wat de rij 0,4 van de sweep al suggereerde.
Een zinvolle volgende stap volgt uit wat je ziet, niet uit de gate: hier clusteren de missers onder de afkapwaarde, dus de kandidaat probeert een lagere. Waren het zelfverzekerde missers geweest (0,07), dan zou de volgende stap bij de features van het model liggen, en zou geen afkapwaarde helpen.
Een kandidaatwijziging, en de vergelijking
binary/candidate.yaml is oloproof.yaml met twee gewijzigde regels:
system:
name: churn-model
version: candidate-cutoff-0.4
callable: app:run_candidateoloproof run --config candidate.yamlGate: BLOCK (exit 3)
│ accuracy-floor │ accuracy │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ recall-floor │ recall │ PASS │ lower_bound_meets_minimum │
│ precision-floor │ precision │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ accuracy │ 79.5% │ [73.2%, 84.9%] │ 159 / 200 observed · 0 missing · 0 excluded │
│ recall │ 89.1% │ [78.7%, 95.5%] │ 57 / 64 observed · 0 missing · 136 excluded │
│ precision │ 62.6% │ [51.8%, 72.6%] │ 57 / 91 observed · 0 missing · 109 excluded │Precies de rij 0,4 van de sweep: recall omhoog, precision omlaag. Exit 3 is INSUFFICIENT_EVIDENCE, niet FAIL: de vloeren liggen binnen de intervallen, dus 200 accounts kunnen niet zeggen aan welke kant de kandidaat zit. De scores veranderden niet, dus Brier, log loss en ROC-AUC zijn identiek.
Vergelijk nu de twee runs case voor case. binary/comparison.yaml bevat vergelijkingsregels:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- {id: recall-no-worse, kind: non_inferiority, metric: recall, margin: 0.05}
- {id: accuracy-no-worse, kind: non_inferiority, metric: accuracy, margin: 0.05}oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy comparison.yamlComparison sha256:... of run_... against run_... · 200 paired cases
accuracy: -9.0 points [-17.9, -1.4] · 200 paired · 0 missing · 0 excluded
recall: +7.8 points [-2.8, +21.9] · 64 paired · 0 missing · 136 excluded
excluded 136: not_a_positive_case
precision: +0.0 points [-8.3, +8.3] · 63 paired · 0 missing · 137 excluded
excluded 137: not_predicted_positive
...
Decisions
recall-no-worse recall non-inferiority, margin 5.0 points PASS lower_bound_above_margin
accuracy-no-worse accuracy non-inferiority, margin 5.0 points INSUFFICIENT_EVIDENCE interval_overlaps_margin
no sample size would make this PASS: the difference itself (-9.0 points) is outside the margin, so more cases would move it toward FAIL
Gate: BLOCK (exit 3)Lees de precision-regel zorgvuldig. De eigen precision van de kandidaat daalde van 82,5% naar 62,6%, maar het gepaarde verschil is +0.0 over 63 paren. Een vergelijking koppelt rijen: een precisionverschil wordt alleen gemeten over accounts die beide modellen markeerden, en op die 63 hadden beide gelijk. De 28 extra accounts die de kandidaat markeerde, waarvan 23 vals alarm, vallen buiten die verzameling. Daarom bewaakt comparison.yaml accuracy in plaats van precision bij een wijziging van de afkapwaarde; Vergelijkingsregels beschrijft de soorten regels.
De beslissing is het nuttige deel: recall is aantoonbaar niet slechter, en accuracy kan niet binnen vijf punten worden aangetoond; de adviesregel zegt dat meer data het richting FAIL zou duwen. Of negen punten accuracy inruilen voor acht punten recall het waard is, is een zakelijke beslissing die de gate zichtbaar heeft gemaakt.
Deel 2: een multiclass-classifier, klasse voor klasse
multiclass/app.py stuurt een supportticket naar een van drie wachtrijen, en heeft één opzettelijke fout: elk ticket dat vanuit de mobiele app wordt verstuurd gaat naar technical.
@system(name="ticket-router", version="baseline")
def route(ticket):
if ticket["channel"] == "app":
return {"queue": "technical"}
return {"queue": classify(str(ticket["subject"]))}Een case:
{"expected": {"queue": "billing"}, "id": "ticket_000", "input": {"channel": "email", "subject": "update the card on file"}, "metadata": {"channel": "email"}}Er is geen multiclass-metriek om aan te zetten. Elke klasse krijgt haar eigen binaire blok: recall voor billing is de binaire recall waarvan de positieve klasse billing is. multiclass/oloproof.yaml:
evaluators:
- {type: predictive_correct, criterion: accuracy, field: queue, expected_field: queue}
- {type: predictive_recall, criterion: recall_billing, field: queue, expected_field: queue, positive: billing}
- {type: predictive_precision, criterion: precision_billing, field: queue, expected_field: queue, positive: billing}
- {type: predictive_recall, criterion: recall_technical, field: queue, expected_field: queue, positive: technical}
- {type: predictive_precision, criterion: precision_technical, field: queue, expected_field: queue, positive: technical}
- {type: predictive_recall, criterion: recall_account, field: queue, expected_field: queue, positive: account}
- {type: predictive_precision, criterion: precision_account, field: queue, expected_field: queue, positive: account}
slices: [metadata.channel]Oloproof berekent hierover geen macro- of micro-gemiddelde. Als je er een nodig hebt, is het een getal dat je zelf afleidt uit de aantallen per klasse, en geen regel kan erop gaten. Het beleid legt een vloer op de klassen die ertoe doen, omdat een router in het geheel accuraat kan zijn en toch één wachtrij kwijtraakt:
rules:
- {id: billing-recall-floor, metric: recall_billing, min: 0.80}
- {id: account-recall-floor, metric: recall_account, min: 0.80}
- {id: technical-precision-floor, metric: precision_technical, min: 0.80}cd ../multiclass
oloproof runGate: BLOCK (exit 1)
│ billing-recall-floor │ recall_billing │ FAIL │ upper_bound_below_minimum │
│ account-recall-floor │ recall_account │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ technical-precision-floor │ precision_technical │ FAIL │ upper_bound_below_minimum │
│ accuracy │ 81.3% │ [74.1%, 87.3%] │ 122 / 150 observed · 0 missing · 0 excluded │
│ recall_billing │ 66.0% │ [51.2%, 78.8%] │ 33 / 50 observed · 0 missing · 100 excluded │
│ precision_billing │ 100.0% │ [89.4%, 100.0%] │ 33 / 33 observed · 0 missing · 117 excluded │
│ recall_technical │ 100.0% │ [92.8%, 100.0%] │ 50 / 50 observed · 0 missing · 100 excluded │
│ precision_technical │ 64.1% │ [52.4%, 74.7%] │ 50 / 78 observed · 0 missing · 72 excluded │
│ recall_account │ 78.0% │ [64.0%, 88.5%] │ 39 / 50 observed · 0 missing · 100 excluded │
│ precision_account │ 100.0% │ [90.9%, 100.0%] │ 39 / 39 observed · 0 missing · 111 excluded │Exit 1 betekent dat minstens één regel FAIL is. Elke klasse heeft haar eigen noemer: 78 tickets werden technical genoemd, dus dat is de noemer van precision voor technical. De slicetabel wijst naar de oorzaak; slices zijn verkennend en gaten nooit, maar een gemarkeerde slice is een spoor dat het lezen waard is:
│ metadata.channel=app │ accuracy │ 42.9% │ [28.8%, 57.8%] │ 21 / 49 observed · ... │ marked (p 0.0001) │oloproof inspect RUN_ID --failures --limit 228 of 150 cases failed, errored or did not finish
ticket_011
output: {"queue": "technical"}
accuracy: failed
precision_technical: failed
recall_account: failed
...De kandidaat (route_candidate, gedraaid met oloproof run --config candidate.yaml) laat de kanaalsnelkoppeling vallen. Op deze synthetische data stuurt hij elk ticket correct door en laat de gate door:
Gate: ALLOW (exit 0)
│ billing-recall-floor │ recall_billing │ PASS │ lower_bound_meets_minimum │
│ account-recall-floor │ recall_account │ PASS │ lower_bound_meets_minimum │
│ technical-precision-floor │ precision_technical │ PASS │ lower_bound_meets_minimum │oloproof compare werkt hier precies zoals in Deel 1, met één verschil per klassemetriek.
Deel 3: een regressor, gescoord op absolute fout
regression/app.py schat het aantal leverdagen en negeert of het artikel op voorraad is:
@system(name="delivery-estimator", version="baseline")
def estimate(order):
return {"days": round(base_days(order), 1)}Een case, met de waarheid als getal:
{"expected": {"days": 11}, "id": "order_000", "input": {"distance_km": 468, "express": false, "in_stock": false}, "metadata": {"in_stock": false}}De enige regressie-evaluator is absolute fout, en die heeft het bereik nodig waarin elk doel ligt. Een absolute fout is een begrensd gemiddelde waarvan het interval alleen binnen dat bereik geldt, dus het wordt gedeclareerd, nooit standaard ingevuld. Leveringen duren hier 0 tot 20 dagen:
evaluators:
- type: predictive_absolute_error
criterion: days_error
field: days
expected_field: days
target_range: [0, 20]
slices: [metadata.in_stock]Het beleid is een budget, een max:-regel: hij slaagt als de bovengrens van het interval erop of eronder ligt.
rules:
- {id: error-budget, metric: days_error, max: 2.0}cd ../regression
oloproof runGate: BLOCK (exit 1)
│ error-budget │ days_error │ FAIL │ lower_bound_above_maximum │
│ days_error │ 2.66 │ [2.01, 3.57] │ mean of 120 observed · 0 missing · 0 excluded │
│ metadata.in_stock=false │ days_error │ 5.18 │ [4.60, 6.65] │ mean of 53 observed · ... │
│ metadata.in_stock=true │ days_error │ 0.67 │ [0.51, 2.19] │ mean of 67 observed · ... │FAIL omdat zelfs de ondergrens van het interval boven het budget van twee dagen ligt. Een score-evaluator heeft geen geslaagd of mislukt per case, dus oloproof inspect RUN_ID --failures toont er geen; lees in plaats daarvan een case:
oloproof inspect RUN_ID --case order_000output: {
"days": 6.1
}
judgments:
days_error: score 4.9De slice zegt waar je moet kijken: bestellingen die niet op voorraad zijn zitten vijf dagen ernaast. De kandidaat (estimate_candidate) telt vijf dagen op voor een artikel dat niet op voorraad is:
oloproof run --config candidate.yamlGate: ALLOW (exit 0)
│ error-budget │ days_error │ PASS │ upper_bound_meets_maximum │
│ days_error │ 0.70 │ [0.56, 1.57] │ mean of 120 observed · 0 missing · 0 excluded │Problemen oplossen
| Symptoom | Oorzaak en oplossing |
|---|---|
| Configuration error: evaluator 'recall' counts 'churned' as the positive class, and no case's 'label' is 'churned' | positive: noemt een waarde die geen case heeft. Gebruik de waarde precies zoals die onder expected staat, inclusief true tegenover "true". |
| release rule ... refers to unknown metric 'false_positives' | Confusion-aantallen zijn geen metrieken. Gate op recall of precision. |
| Een regressie-evaluator wordt voor de run geweigerd | target_range ontbreekt of is leeg. Declareer het bereik dat de doelen echt kunnen aannemen; een breder bereik geeft een breder interval. |
| Een rangschikkingsregel leest INSUFFICIENT_EVIDENCE (interval_unavailable) | De suite is te klein voor het interval van die statistiek, typisch pr_auc. Gate op roc_auc, of voeg cases toe. |
| De kandidaat rapporteert de getallen van de baseline | Beide runs delen een version, dus de gecachete voorspellingen werden hergebruikt. Geef elke wijziging een eigen versie. |
| Een case is missing in plaats van fout | Je functie wierp een fout op, of het veld dat de evaluator leest ontbreekt of is geen getal. oloproof inspect RUN_ID --case ID toont de fout. |
Beperkingen
- Geen aanbevolen drempel. De sweep rapporteert wat elke gedeclareerde afkapwaarde zou hebben gemeten.
- Geen macro- of micro-gemiddelde als metriek voor multiclass, en geen multilabelondersteuning buiten één blok per label.
- Regressie is alleen absolute fout binnen een gedeclareerde target_range: geen kwadratische fout, R kwadraat of onbegrensde fout.
- Kalibratie wordt als tabel getoond, niet gegate en niet gecorrigeerd.
- Een gepaard precisionverschil dekt alleen rijen die beide modellen markeerden, zoals Deel 1 laat zien.
- De voorbeelden zijn deterministisch en synthetisch. Hun beslissingen tonen de mechaniek, niet hoe een echt model zich op echte data gedraagt.
Waar verder
- Classifiers en regressors is de referentie veld voor veld.
- Een kandidaat met een baseline vergelijken en Vergelijkingsregels behandelen de vergelijkingswerkwijze.
- Slices behandelt confidence:-banden en slicesupport.
- CI gaten noemt de exitcodes.