diff --git a/README.md b/README.md index b978638..7898d54 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,36 @@ where `x̂ = γ · LN(x) + γₓ · x` (scaled LayerNorm, `γ` init `10⁻⁶`, where `A ∈ ℝ^{r×d}`, `B ∈ ℝ^{d×r}`, `r = 4` +### Pair formation and negative sampling + +The dataset provides only **positive pairs** — each entry maps one drone image to its +matching satellite crop(s). Negative pairs are **not** stored explicitly; instead, they +are constructed automatically inside each training batch via the InfoNCE loss: + +``` +Batch (B = 8): + drone_0 ↔ sat_0 ← positive (from dataset) + drone_1 ↔ sat_1 ← positive + ... + drone_7 ↔ sat_7 ← positive + +Similarity matrix B×B: + sat_0 sat_1 ... sat_7 + q_0 [ pos neg ... neg ] ← CE target = 0 + q_1 [ neg pos ... neg ] ← CE target = 1 + ... + q_7 [ neg neg ... pos ] ← CE target = 7 +``` + +- **Positives:** diagonal `sim[i, i]` — correct drone-satellite pair +- **Negatives:** off-diagonal `sim[i, j≠i]` — other satellite images in the batch (in-batch negatives) +- At `batch_size=8`: 1 positive + 7 in-batch negatives per query +- No hard-negative mining — negatives are random within the batch +- Larger batch → more negatives → harder contrastive task + +Each drone image may have multiple satellite candidates (with distance-based weights). +At training time, one satellite crop is sampled per drone (weighted if semi-positive). + ### Loss function Symmetric InfoNCE with learnable temperature (CLIP-style `logit_scale`):