When fits run
- The first run starts within a few minutes of the first outcome you post, or of the judgment’s first outcome rules. A run makes a fit only for an epoch that meets the minimums, so the first run usually makes none.
- Until there is a fit, Brussle runs again every hour (for the current epoch, and for a new epoch with no fit yet), so the first fit comes within the hour after your outcomes meet the minimums.
- Once there is a fit, Brussle makes a new fit each night on all the outcomes so far, rule outcomes included. The new fit replaces the current fit only under the rule.
- When Brussle judges answers again to correct them, the next run starts within a few minutes after the correction ends (see when answers are corrected).
Fit now
To fit at once (right after you post the outcomes that meet the minimums, for example), callPOST /namespaces/{ns}/judgments/{name}/calibration/fit (fitCalibration, fit_calibration) or select Fit now in the judgment’s Calibration section. The run usually ends within a minute or two. Then read the report.
- You can ask once an hour for each judgment, and named runs share this limit. Within that hour the call returns
rate_limited(429), andRetry-Aftergives the seconds until Brussle accepts the next call. - If a run is already waiting or running, that run serves the call.
- Fit now runs on every plan, and it is never billed.
The rule
The rule compares the challenger with the current fit, which is a higher bar than the test every fit passes against the engine’s raw numbers (from 100 outcomes, any win over them applies; see below 100 outcomes). The challenger replaces the current fit only when both are true:- It is clearly better on the outcomes that arrived since the current fit was made. The current fit never saw them, and the challenger is scored on each one as a fit that never saw it, so they are a fair test of both. The challenger must beat the current fit on them by more than chance would explain.
- It moves the number by at least a point: 0.01 on average over all the judgment’s outcomes. For a
choiceorscore, the move is the probability that moves between the options.
fit id, and nothing is judged again.
- An outcome arrives when you post it, not when it was observed. An implicit negative arrives when its window closes. After the engine changes, a replayed outcome arrives when it is replayed. An outcome whose answer was judged again with changed inputs counts as new too.
- The current fit is withdrawn when the engine’s raw numbers beat it by the same margin on the outcomes since. Answers then read the night’s fit, or the raw numbers if that fit did not beat them. The run says
withdrawn: fallback_better. - A fit held for its labels withdraws the current one. When the night’s fit is held because the labels cannot support a correction,
truncated_sampleorlabels_read_like_the_outcomeon the report, the current fit was made on those same labels and is withdrawn without the test: answers read the engine’s numbers, and the run sayswithdrawn: labels_held. Nothing waits for new outcomes; the next run after the labels cover the live answers, or stop reading like the outcome, fits again. A pinned fit stays pinned. - A new start takes the night’s fit at once: the first fit, a new engine epoch, a new version, and a change from fitting on all outcomes to fitting on labelling-queue labels alone, or back.
- Below 100 outcomes (while the current fit or the night’s rests on fewer than 100), each night’s fit replaces the current one under the margin below 100 rather than this test.
- When a run cannot fit again, because outcomes were deleted and the epoch fell below the minimums, the current fit stays.
- It applies to every judgment: to
calibratedon abool, achoiceand ascore, topof a judgment with features, and to a template tenant’s own fit and its own weights, on that tenant’s own outcomes. The exception is a choice among candidates with features, where each run’s fit replaces the current one, as a first fit does. - On a steady process, the rule rarely changes the fit by chance. A real change in your outcomes (a new base rate, say) shows in the outcomes since, and the new fit replaces the current one once it is clearly better.
Read the current fit and the challenger
Each epoch of the report shows both:current: the fit answers read now. Itsfitis its id, ornullwhen it did not beat the raw numbers, andrunis the run that holds it.pinnedistruefor a pinned fit, andpin_suspendedisolder_preprocessingwhen the pin is suspended.fitted_atis when the run that made the fit ran. A fit kept since an earlier run keeps that date, so it can be weeks older than the newest run.sincemeasures the fit against the raw numbers on the outcomes that arrived after it was made:outcomes,fallback_log_loss(the raw numbers’),log_loss(the current fit’s) and thestandard_errorof the difference.sinceisnullwhile those outcomes hold fewer than 20 of each kind, and on the night the fit was made.currentisnullwhile the epoch has no fit of its own.challenger: the newest run’s fit when it did not replace the current one, andnullwhen it did or when there was no current fit. It has its ownfit,run,fitted_at,outcomesandheld_out, andversus_current, which sets the challenger against the current fit on the outcomes since.gainis the current fit’s log loss minus the challenger’s, so a positivegainfavours the challenger.shiftis how far the challenger would move the number on average over every outcome.gainandstandard_errorarenullwith fewer than 20 of each kind since. On achoiceorscorewith features,moved_picksis the share of outcomes whose most probable option would change.
tenant.current and tenant.challenger say the same of the tenant’s own fit, against the pool (fallback: pooled). See per-customer corrections.
Why a new fit was held
challenger.held says why the current fit stays:
too_few_outcomes_since: fewer than 20 of each kind arrived since the current fit was made. Post more outcomes, or wait for them.not_clearly_better: the new fit did not beat the current one clearly on the outcomes since (versus_current.gainandstandard_errorgive the margin). The difference is within noise.too_small_a_move: the new fit is better but would move the number by less than 0.01 on average (versus_current.shift). On a judgment with features each move re-judges the judgments that readp, billed, so a move this small is not worth it.pinned: a pin holds the fit.
current.since tells whether the current fit still beats the raw numbers on outcomes it never saw; when it stops doing so by the same margin, the next run withdraws it.
Promote the newest fit
To make the newest fit current for every version and epoch now, whether or not it is clearly better, call Fit now withpromote:
- The run makes its own new fit and applies it. It fits all the outcomes so far, as a nightly run does, and that fit becomes current for each version and epoch that is not pinned, tenants included. A promote never moves a pin.
- With readers, confirm first. On a judgment with features (a
bool, achoiceor ascore) that other judgments read, a new current fit judges their documents again, billed. Withoutconfirm, the call returns thedownstreamestimate (200), made against last night’s fits, and queues nothing. Withconfirm: true, Brussle queues the run, and the answer carries the estimate too. - A confirmed promote is not held by the hourly limit. You accepted its cost, so Brussle queues it even within an hour of Fit now. Without readers, Brussle queues a promote at once, and it counts as Fit now’s request for the hour.
- Templates. On a namespace that inherits the judgment, a promote is
invalid_request; promote on the template. Fit now without a body still queues the template’s run.
Pin a fit
Each answer reads the current fit for its version and epoch by default (auto). A pin holds one fit you choose for one version and epoch, to hold a fit you checked or to go back to an earlier one. Name the fit as the list of fits shows it, by its run, version, engine and engine_version. fit is optional; when you give it, Brussle checks that it is that run’s fit.
- Send
calibrationalone in itsPATCH /namespaces/{ns}/judgments/{name}, without other settings, or the request isinvalid_request. - The list replaces all the pins. Include the pins you want to keep. A pin sent again from the same run stays as it is, and two pins for one version and epoch are
invalid_request. - The pin writes a run (
cause: pin) that holds the pinned fit, and every later run holds it too. The response has the judgment, withcalibration: {"mode": "pinned", "pins": [...]}, and each pin haspinned_at.modeisautowhen no pin applies. - Answers of that version and epoch read the pinned fit, also when a later fit would do better. Every other version and epoch stays
auto. Each run still makes a fit and lists it as the challenger, and when that fit clearly beats the pinned fit on the outcomes since, the report warnspinned_fit_beaten. - A pin is a setting, like thresholds. It makes no version, and a new version starts in
auto. - On any judgment without features, a pin changes only
calibrated. Readers use the raw numbers, so nothing is judged again or billed, and there is nothing to confirm.
confirm, the response is only the downstream estimate, and nothing changes. With confirm: true, the pin applies and a resync job starts; the response has the judgment, job_id and downstream. The dashboard shows the cost first and asks you to confirm.
Brussle refuses a pin with invalid_request when:
- the run is not in the judgment’s list of fits
- the run has no fit for that version and epoch (for example, because it did not beat the raw numbers)
fitis not that run’s fit- the fit uses features and was made before Brussle last changed how it keeps features
- the namespace inherits the judgment from a template (set the pin on the template, as per-customer corrections shows)
conflict. Read the judgment and send the list again.
A pin and a new epoch
A pin never reaches another epoch. After the engine changes by itself, the new epoch starts inauto. Until the new epoch has its own fit, its answers read the previous epoch’s fit as usual, which here is the pinned fit, with "from_previous_epoch": true and "pinned": true. The 30-day limit for calibrated applies as for any fit, and p of a bool with features keeps the pinned fit until the new epoch has its own.
A suspended pin
Brussle sometimes changes how it keeps features, and a fit with features made before the change cannot read the features after it. At the next run, that fit’s version and epoch readauto: the pin shows "suspended": "older_preprocessing", the answer shows "pinned": false, and the report warns pin_suspended. Pin a fit made since, or lift the pin. A pin of a fit without features is never suspended.
Lift a pin
To lift a pin, send the list of pins without it.{"calibration": {"pins": []}} lifts them all:
- The next run comes at once. Brussle brings it forward, usually within a few minutes. It does not count toward Fit now’s limit, and the list of fits shows it as
cause: nightly. - Until that run ends, answers read the pinned fit and still show
"pinned": true.GETon the judgment already showsmode: auto. - A lift keeps the number until a clearly better fit arrives. The formerly pinned fit stays current, now in
auto, and that run’s fit replaces it only when it is clearly better on the outcomes since the pinned fit was made (usually many by then). To take tonight’s fit anyway, promote. On a judgment with features, when the fit its answers read changes, Brussle re-judges the readers and bills for them, with no confirm.
replay.status is done when the replay has ended.
Events when the fit changes
On a judgment with features (abool, a choice or a score), the answer’s numbers are the corrected ones and readers act on them. When the fit they read changes (its first fit, a promotion, a withdrawal, a pin or a lift), Brussle brings the judgments that read it up to date: it judges again the documents whose view of it changed, billed like any re-judge. A night that keeps the current fit re-judges nothing. Then:
judgment.fit_appliedreports how many documents crossed each of the judgment’s thresholds. When the numbers go back to the engine’s, the event isjudgment.fit_withdrawn. Both carryfit,runandpinned.- The event gives each reader’s re-judges:
engine_judgments, which are billed, andreuses(re-judges where only a feature the reader keeps moved), which use their engine answer again at no charge. - The counts come from those re-judges, so the event arrives after they have started.
calibrated, so a new fit judges nothing again.