blinklinmult.fit¶
Fit an operating point for a model that never had one.
BlinkCNN’s thresholds were fitted on validation during training. The 1.x
models were released as weights alone, so MODELS
records the plain 0.5 default for them – a placeholder, not a measurement.
That gap makes event comparisons between the generations unfair: v2’s operating point was tuned and theirs was not. This module closes it. Give it scored recordings with their annotated blinks and it sweeps the same grids the training callback uses, returning the pair that maximises event F1:
from blinklinmult.fit import fit_operating_point
point = fit_operating_point(signals, annotations, fps=30.0)
detector.spec = point.applied_to(detector.spec)
Fit on validation, never on test. A threshold chosen on the same recordings it is reported on is not a measurement, it is a description of that split – and it will not survive contact with new data. The function does not enforce this; it cannot see where the signals came from.
Hysteresis usually wins, so it is searched by default. A single cut has to be
low enough to catch the shallow start of a closure and high enough to ignore
noise, and no one value does both. Two thresholds split those jobs: the high one
decides whether a run is a blink, the low one decides where it starts and
ends. On BlinkCNN this was worth 0.19 to 0.52 event F1.
Attributes¶
Module-level logger. |
|
Low-threshold fractions searched, mirroring the training callback's grid. |
Exceptions¶
Raised when there is not enough data to fit an operating point. |
Classes¶
A fitted decision rule and the score it achieved. |
Functions¶
|
Search for the threshold pair that maximises event F1. |
Module Contents¶
- blinklinmult.fit.logger¶
Module-level logger.
- blinklinmult.fit.DEFAULT_LOW_RATIOS: tuple[float | None, ...]¶
Low-threshold fractions searched, mirroring the training callback’s grid.
Nonecomes first so a single threshold wins ties: hysteresis costs a second parameter, and it should have to earn its place rather than be adopted on a rounding difference.
- exception blinklinmult.fit.FitError[source]¶
Bases:
ExceptionRaised when there is not enough data to fit an operating point.
Initialize self. See help(type(self)) for accurate signature.
- class blinklinmult.fit.OperatingPoint[source]¶
A fitted decision rule and the score it achieved.
- Parameters:
threshold (float) – The high threshold – whether a run is a blink.
low_ratio (float | None) – Low threshold as a fraction of
threshold, orNonefor a single cut.f1 (float) – Event F1 at this point, on the data it was fitted to.
precision (float) – Event precision there.
recall (float) – Event recall there.
- property low_threshold: float | None¶
The absolute low threshold, or
Nonefor a single cut.
- applied_to(model_spec: blinklinmult.registry.ModelSpec) blinklinmult.registry.ModelSpec[source]¶
Copy a spec with this operating point substituted in.
- blinklinmult.fit.fit_operating_point(signals: list[numpy.ndarray], annotations: list[list[blinklinmult.train.events.Interval]], thresholds: numpy.ndarray | None = None, low_ratios: tuple[float | None, ...] | None = None) OperatingPoint[source]¶
Search for the threshold pair that maximises event F1.
- Parameters:
signals (list[np.ndarray]) – One per-frame score array per recording, in
[0, 1]. Produced byscore().annotations (list[list[Interval]]) – The annotated blink intervals for each, in the same order.
thresholds (np.ndarray | None) – High thresholds to try;
DEFAULT_THRESHOLDSby default.low_ratios (tuple[float | None, ...] | None) – Low ratios to try;
DEFAULT_LOW_RATIOSby default.
- Returns:
The best pair found, with its scores.
- Return type:
- Raises:
FitError – If the inputs disagree in length, are empty, or hold no annotated blink to fit against.