Focal Loss Viz
Plot of the focal loss FL(p_t) = -(1 - p_t)^γ · log(p_t) against model confidence, with the cross-entropy curve underneath for comparison. Drag γ and easy examples on the right side of the chart collapse toward zero loss while hard examples on the left hold nearly full weight — the same lever RetinaNet pulls to make dense object detection actually train.
Focal loss versus standard cross-entropy.Focal loss with γ = 2.00. At p_t = 0.9 the modulating factor is 0.010, suppressing the cross-entropy loss by 100×.
FL = -(1 - p_t)^γ · log(p_t)γ = 2.00
Modulating factor (1 - p_t)^γ0 ≤ (1 - p_t)^γ ≤ 1
2.00
Customize
Focusing parameter
2.00
Display
Installation
npx shadcn@latest add https://craftbits.dev/r/focal-loss-viz.jsonUsage
import { FocalLossViz } from "@craft-bits/core";
<FocalLossViz defaultGamma={2} />Show the modulating factor on a second mini-chart so the user can read off (1 - p_t)^γ at any p_t:
<FocalLossViz defaultGamma={2} showModulator />Drive γ from the outside:
const [gamma, setGamma] = useState(2);
<FocalLossViz
gamma={gamma}
onGammaChange={setGamma}
gammaRange={[0, 8]}
/>Understanding the component
- The focal-loss recipe. Cross-entropy
-log(p_t)punishes wrong predictions hard but keeps a non-trivial gradient on already-confident examples — useless on imbalanced datasets where most examples are easy negatives. Multiplying by(1 - p_t)^γdrives that gradient toward zero asp_t → 1, concentrating training on the few hard examples that still look like noise to the model. - γ as a focusing dial. At γ = 0 the modulating factor is identically 1 and focal loss is exactly cross-entropy — the curves overlap. As γ grows the focal curve peels away from CE on the easy (right) side. The hard (left) side barely moves because
(1 - p_t)^γ ≈ 1whenp_tis small. - Numerical stability.
log(p_t)is computed vialog(max(p_t, 1e-10))so the left edge of the chart stays finite. Curve sampling clips segments that would shoot past the y-axis ceiling, drawing a clean break instead of a wild jump. - Spring transitions on γ changes. The focal curve animates with
SPRINGS.smoothso dragging γ produces a continuous reshape rather than a chatter of redraws. Reduced-motion users snap. - Controlled + uncontrolled. Pass
gamma+onGammaChangeto drive γ from outside, or omit both and let the component own its state viadefaultGamma. - Optional modulator subplot. Setting
showModulatoradds a narrow second chart of(1 - p_t)^γon a fixed 0..1 axis. Useful when teaching what's being multiplied in — the loss curve alone hides the magnitude of the down-weighting at any givenp_t.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
gamma | number | — | Controlled γ ≥ 0. Pair with onGammaChange. |
defaultGamma | number | 2 | Uncontrolled initial γ. |
onGammaChange | (γ: number) => void | — | Fires whenever the γ slider commits a new value. |
gammaRange | readonly [number, number] | [0, 5] | Slider extents. Both must be ≥ 0, min < max. |
showModulator | boolean | false | Render the (1 - p_t)^γ curve on a second mini-chart. |
transition | Transition | SPRINGS.smooth | Spring for the focal-curve reshape. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- The chart is wrapped in
role="figure"with a dynamicaria-describedby("Focal loss with γ = 2.00. At p_t = 0.9 the modulating factor is 0.010, suppressing the cross-entropy loss by 100×.") so screen-reader users get the same headline as sighted users. - The γ slider is a
LabeledSliderbacked by a native<input type="range">— full keyboard support out of the box (Arrowkeys step,Page Up/Page Downstep by 10%,Home/Endjump to the extents). - The summary uses
aria-live="polite"so changes are announced as the user drags. - Curve transitions use
SPRINGS.smooth;prefers-reduced-motion: reducesnaps instantly.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/FocalLossViz.tsx). Stripped the lesson-specific Explore / Predict / Challenge mode strip,Widgetchrome,p_tscrubber, suppression-ratio chip, and undo / redo history; generalised to a single visualisation primitive with controlled / uncontrolled γ. Added the optional modulator subplot, replaced the hand-rolled pointer-event slider withLabeledSlider(free keyboard + a11y), and swapped inline springs forSPRINGS.smoothfrom@craft-bits/core/motionso reshapes feel continuous on slow drags. - Upstream paper: Lin et al., Focal Loss for Dense Object Detection (2017).