Update README.md
Browse files
README.md
CHANGED
|
@@ -1,9 +1,360 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
---
|
| 4 |
-
GEOLIP has finally evolved to a stable state through heavy experimentation and development.
|
| 5 |
|
| 6 |
-
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
---
|
|
|
|
| 4 |
|
| 5 |
+
# GeoLIP Core β Geometric Linear Interpolative Patchwork
|
| 6 |
|
| 7 |
+
GeoLIP is a geometric deep learning framework built on a single premise: **the unit hypersphere has structure, and that structure is computable**. Rather than scaling parameters to approximate statistical boundaries between classes, GeoLIP places learnable reference points (anchors) on S^(d-1) and measures angular relationships between embeddings and those anchors. The resulting distance patterns β interpreted through compartmentalized patchwork networks β produce compact, reusable geometric features that generalize without requiring billions of parameters.
|
| 8 |
+
|
| 9 |
+
This repository contains the stable core: three files, 1315 lines, covering every component from anchor initialization through magnitude prediction to the full three-domain training objective. Every piece is composable, every loss is interchangeable, and every design decision traces back to empirically validated geometric principles.
|
| 10 |
+
|
| 11 |
+
## Architecture Overview
|
| 12 |
+
|
| 13 |
+
```
|
| 14 |
+
Pixels β ConvEncoder β features β L2 normalize β S^(d-1)
|
| 15 |
+
β
|
| 16 |
+
MagnitudeFlow (relay stack, no attention)
|
| 17 |
+
β β
|
| 18 |
+
per-anchor magnitude embedding
|
| 19 |
+
β β
|
| 20 |
+
tri = (1 - cos) Γ magnitude cos β soft assignment
|
| 21 |
+
β β
|
| 22 |
+
Patchwork soft_assign
|
| 23 |
+
(compartments) β
|
| 24 |
+
β ββββββββ΄βββββββ
|
| 25 |
+
bridge prediction task head β logits
|
| 26 |
+
(reads all three)
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
Three independent loss domains shape the model cooperatively:
|
| 30 |
+
|
| 31 |
+
- **External** (task): cross-entropy + embedding InfoNCE
|
| 32 |
+
- **Geometric** (structure): patchwork InfoNCE + bridge
|
| 33 |
+
- **Internal** (self-organization): assignment crispness, triangulation consistency, attraction, CV regularization, anchor spread
|
| 34 |
+
|
| 35 |
+
The constellation discovers its own Voronoi structure through internal losses. The task head reads that structure but does not write to it. This separation prevents classification shortcuts from hijacking anchor geometry.
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
## File 1: `geolip_core.py` β Geometric Building Blocks
|
| 40 |
+
|
| 41 |
+
Everything structural. No losses, no training loops. Pure composable components that can be assembled into any geometric architecture.
|
| 42 |
+
|
| 43 |
+
### Activations
|
| 44 |
+
|
| 45 |
+
**`SquaredReLU`** β `x β ReLU(x)Β²`. Empirically the strongest activation across all GeoLIP patchwork configurations. The squaring amplifies separation between active and inactive neurons, producing sharper compartment specialization than GELU or standard ReLU. Used as the default throughout.
|
| 46 |
+
|
| 47 |
+
**`StarReLU`** β `x β ReLU(x)Β² Γ scale + bias` with learnable scale and bias. Runner-up to SquaredReLU in bulk activation tests. The learnable parameters allow the activation to adapt its dynamic range per layer, useful when compartments operate at different magnitude scales.
|
| 48 |
+
|
| 49 |
+
**`make_activation(name)`** β Factory function. Supports `squared_relu`, `star_relu`, `gelu`, `relu`, `sigmoid`. Every patchwork and task head references activations by name through this factory, making architecture-wide activation swaps trivial.
|
| 50 |
+
|
| 51 |
+
### Anchor Initialization
|
| 52 |
+
|
| 53 |
+
Anchor placement on S^(d-1) at initialization determines the starting Voronoi tessellation. Poor initialization can leave large regions of the sphere unmeasured.
|
| 54 |
+
|
| 55 |
+
**`init_anchors_xavier(n, d)`** β Xavier normal, then L2-normalize. Fast, reasonable for moderate anchor counts. Near-orthogonal in high dimensions by concentration of measure.
|
| 56 |
+
|
| 57 |
+
**`init_anchors_orthogonal(n, d)`** β QR decomposition for exact orthonormal basis when n β€ d. When n > d, fills the first d anchors with the orthonormal basis and adds random normalized vectors for the remainder. Guarantees zero mutual cosine similarity for the first d anchors.
|
| 58 |
+
|
| 59 |
+
**`init_anchors_repulsion(n, d)`** β QR initialization followed by 200 iterations of nearest-neighbor repulsion. Each step pushes every anchor away from its closest neighbor, producing even coverage of S^(d-1). The proven default. Costs ~50ms at initialization but produces measurably better early-training geometry than Xavier or orthogonal alone.
|
| 60 |
+
|
| 61 |
+
### Constellation
|
| 62 |
+
|
| 63 |
+
**`Constellation(n_anchors, dim, anchor_drop, anchor_init)`**
|
| 64 |
+
|
| 65 |
+
The fundamental measurement instrument. Anchors are learnable parameters living on S^(d-1), re-normalized every forward pass. Triangulation computes cosine similarity between each embedding and every anchor, producing an angular distance profile that uniquely identifies the embedding's position on the sphere.
|
| 66 |
+
|
| 67 |
+
*Why it exists*: A single embedding vector is a point. A triangulation against N anchors is a measurement β it describes where that point sits relative to N known reference positions. This is the difference between "a location" and "a location on a map." The patchwork reads the map, not the location.
|
| 68 |
+
|
| 69 |
+
*Anchor dropout*: During training, randomly masks a fraction of anchors (default 15%). Forces the patchwork to develop redundant measurement pathways rather than depending on any single anchor. Proven to improve generalization.
|
| 70 |
+
|
| 71 |
+
*Critical rule*: Anchors are always L2-normalized before use. They live on S^(d-1), not in ambient R^d. Weight decay must be disabled for anchor parameters β decay pulls them toward the origin, destroying their geometric meaning.
|
| 72 |
+
|
| 73 |
+
### Patchwork
|
| 74 |
+
|
| 75 |
+
**`Patchwork(n_anchors, n_comp, d_comp, activation)`**
|
| 76 |
+
|
| 77 |
+
Round-robin compartmentalized interpreter. Each compartment reads a disjoint subset of anchor distances (anchor k goes to compartment k % n_comp) through a 2-layer MLP with LayerNorm.
|
| 78 |
+
|
| 79 |
+
*Why it exists*: Raw triangulation distances are high-dimensional and redundant. The patchwork compresses them into compartment-specific features, where each compartment specializes in a different angular region of the sphere. This specialization is enforced by architecture (non-overlapping anchor subsets) and verified empirically (compartment correlation < 0.15 in trained models).
|
| 80 |
+
|
| 81 |
+
*Why round-robin*: Consecutive anchors tend to cluster spatially after push operations. Round-robin assignment (0βcomp0, 1βcomp1, ..., 8βcomp0, 9βcomp1, ...) ensures each compartment receives anchors distributed across the sphere rather than spatially concentrated. This maximizes measurement diversity per compartment.
|
| 82 |
+
|
| 83 |
+
*Output*: `(B, n_comp Γ d_comp)` β a structured geometric descriptor with independently interpretable compartments.
|
| 84 |
+
|
| 85 |
+
### RelayLayer
|
| 86 |
+
|
| 87 |
+
**`RelayLayer(input_dim, patch_dim, n_anchors, n_phases, pw_hidden, gate_init)`**
|
| 88 |
+
|
| 89 |
+
The core geometric processing primitive. Replaces attention entirely. Operates on S^(patch_dim-1) (default S^15, where the CV attractor is a geometric fact).
|
| 90 |
+
|
| 91 |
+
Pipeline per forward pass:
|
| 92 |
+
1. LayerNorm the input
|
| 93 |
+
2. Reshape into P patches of dimension `patch_dim`
|
| 94 |
+
3. L2-normalize each patch to S^(patch_dim-1)
|
| 95 |
+
4. Triangulate against per-patch anchors at 3 SLERP phases (t=0, 1/3, 2/3)
|
| 96 |
+
5. Independent patchwork MLP per patch interprets the triangulation
|
| 97 |
+
6. Gated residual: `gate Γ patchwork_output + (1-gate) Γ input_patch`
|
| 98 |
+
7. Global skip connection: `input + blended_output`
|
| 99 |
+
|
| 100 |
+
*Why it exists*: Attention (softmax-weighted averaging) destroys angular structure. Each attention layer without residual halves effective dimensionality (measured: 62β28). The relay measures and gates β it never averages angular relationships. Empirically preserves 99.4% cosine similarity at depth 16, compared to 7.4% for attention.
|
| 101 |
+
|
| 102 |
+
*SLERP stroboscope*: Each relay layer's anchors interpolate between their home position (initialization) and their current learned position via spherical linear interpolation. Triangulating at 3 phases along this path provides angular measurements that no single-shot triangulation can capture β the rate of change of distance as the anchor moves reveals curvature information invisible to static measurement.
|
| 103 |
+
|
| 104 |
+
*Cold gating*: Gates initialize at sigmoid(-3) β 0.047. At initialization, the relay is nearly transparent β 95.3% of the signal passes through unchanged. This means stacking N relays at init is approximately the identity function. The gates open during training only where the relay's geometric processing provides useful signal. This prevents early training instability from deep stacks.
|
| 105 |
+
|
| 106 |
+
*Critical rule*: Relay anchors (like constellation anchors) must be excluded from weight decay. They live on per-patch spheres S^(patch_dim-1).
|
| 107 |
+
|
| 108 |
+
### ConstellationRelay
|
| 109 |
+
|
| 110 |
+
**`ConstellationRelay(dim, n_anchors, n_comp, d_comp, gate_init, anchor_init, activation)`**
|
| 111 |
+
|
| 112 |
+
Sequence-aware wrapper around the relay concept, using the full Constellation + Patchwork pipeline instead of the einsum-based RelayLayer. Handles both `(B, D)` and `(B, S, D)` inputs, making it usable as a drop-in replacement for attention layers in any transformer-like architecture.
|
| 113 |
+
|
| 114 |
+
*Why both RelayLayer and ConstellationRelay exist*: RelayLayer is optimized for the MagnitudeFlow's internal processing (fixed patch_dim=16, einsum-based, maximum throughput). ConstellationRelay is the general-purpose version that accepts arbitrary dimensions and sequence inputs.
|
| 115 |
+
|
| 116 |
+
### MagnitudeFlow
|
| 117 |
+
|
| 118 |
+
**`MagnitudeFlow(dim, n_anchors, hidden_dim, n_heads, n_layers, mag_min, mag_max, n_comp)`**
|
| 119 |
+
|
| 120 |
+
Per-compartment magnitude prediction through a stack of RelayLayers. No attention anywhere in the stack.
|
| 121 |
+
|
| 122 |
+
The core insight: L2 normalization projects embeddings onto S^(d-1), destroying magnitude information. But the pre-normalization magnitude carries signal β it reflects encoder confidence. MagnitudeFlow recovers this signal geometrically: it takes the embedding direction, the triangulation profile, and the raw magnitude as context, processes them through N relay layers on S^15, and outputs per-compartment magnitude scalars.
|
| 123 |
+
|
| 124 |
+
These scalars weight the triangulation distances per compartment before the patchwork reads them: `tri_weighted = tri Γ magnitude`. Compartments receiving higher magnitude become more influential in the patchwork's interpretation. This gives the model a per-region confidence mechanism without any attention.
|
| 125 |
+
|
| 126 |
+
*Architecture*:
|
| 127 |
+
```
|
| 128 |
+
emb_proj(dimβrelay/2) + tri_proj(Aβrelay/4) + raw_mag(1) β ctx_proj β relay_dim
|
| 129 |
+
β RelayLayer 1 (own anchors on S^15) β gated residual β skip
|
| 130 |
+
β RelayLayer 2 (own anchors on S^15) β gated residual β skip
|
| 131 |
+
β ...
|
| 132 |
+
β reshape to (B, n_comp, 16) β per-compartment MLP β sigmoid β [mag_min, mag_max]
|
| 133 |
+
β expand to per-anchor (B, A)
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
*Stats bias*: After each anchor push operation, MagnitudeFlow receives per-compartment momentum statistics from the push. These are added as a bias to the magnitude output, allowing the magnitude to account for how rapidly the anchor field is changing in each region.
|
| 137 |
+
|
| 138 |
+
*Why per-compartment, not per-anchor*: Per-anchor magnitude (512+ scalars) is too fine-grained β the model hallucinates confidence at individual anchor resolution. Per-compartment (4-8 scalars) is coarse enough to represent regional confidence without overfitting to individual anchor positions.
|
| 139 |
+
|
| 140 |
+
### AnchorPush
|
| 141 |
+
|
| 142 |
+
**`AnchorPush(strategy, n_anchors, dim, **kwargs)`**
|
| 143 |
+
|
| 144 |
+
Periodically repositions anchors toward class centroids computed from accumulated embeddings. Three strategies:
|
| 145 |
+
|
| 146 |
+
**`raw`**: Fixed learning rate blend toward target. `anchor = normalize(anchor + lr Γ (target - anchor))`. Simple, effective, no state.
|
| 147 |
+
|
| 148 |
+
**`gru`**: Statistics-gated SLERP. Maintains EMA of utilization and drift per anchor. Update gate z scales with misalignment + underuse. Reset gate r controls blending with previous position. Produces variable-speed updates: underused anchors move faster, well-placed anchors stay put.
|
| 149 |
+
|
| 150 |
+
**`momentum`**: SGD with momentum on S^(d-1). Accumulates residuals in tangent space with configurable decay. Reprojection onto the tangent plane at each step keeps the accumulator geometrically valid. Dead anchors (utilization below floor) receive forced correction. The proven default for production training.
|
| 151 |
+
|
| 152 |
+
*Why push exists*: Anchors are learnable parameters that receive gradient from internal losses (spread, attraction, assignment). But gradient-based anchor movement is slow and can get trapped in local optima. Push provides a periodic global correction based on the actual class structure of the embedding space, analogous to batch normalization providing periodic mean/variance correction.
|
| 153 |
+
|
| 154 |
+
*Critical rule*: Push writes directly to `anchors.data`, bypassing the optimizer. It operates in parameter space, not gradient space. The optimizer's momentum and adaptive learning rates for anchor parameters are therefore stale after each push β this is intentional. The optimizer handles fine local adjustment; push handles coarse global repositioning.
|
| 155 |
+
|
| 156 |
+
### FlowAttention (Historical)
|
| 157 |
+
|
| 158 |
+
**`FlowAttention(dim, n_anchors, flow_dim, n_steps, time_dim, gate_init)`**
|
| 159 |
+
|
| 160 |
+
3-step Euler ODE integration in the tangent plane of S^(d-1), conditioned on sinusoidal timestep embeddings and push statistics. Achieved 69.8% single-view accuracy on CIFAR-100 but was superseded by the relay architecture, which provides equivalent geometric processing without the ODE overhead and with better depth scaling.
|
| 161 |
+
|
| 162 |
+
Retained for backward compatibility with existing checkpoints. New architectures should use RelayLayer or ConstellationRelay.
|
| 163 |
+
|
| 164 |
+
### GeometricAutograd
|
| 165 |
+
|
| 166 |
+
**`GeometricAutograd`** β A custom autograd Function that is the identity in the forward pass but modifies gradients in the backward pass. Two corrections:
|
| 167 |
+
|
| 168 |
+
1. **Tangential projection**: Attenuates the radial component of gradients (the component pointing toward/away from the origin). On S^(d-1), only tangential movement is meaningful β radial gradients push the embedding off the sphere, which L2 renormalization then undoes. Removing them before they accumulate in optimizer state reduces wasted momentum.
|
| 169 |
+
|
| 170 |
+
2. **Anchor separation**: Projects out the component of the gradient pointing toward the nearest anchor. This prevents embeddings from collapsing onto their nearest anchor, maintaining measurement diversity.
|
| 171 |
+
|
| 172 |
+
### Utilities
|
| 173 |
+
|
| 174 |
+
**`param_count(module, name)`** β Counts total and trainable parameters. Prints a formatted line when name is provided.
|
| 175 |
+
|
| 176 |
+
**`model_summary(model)`** β Prints per-submodule parameter breakdown. Essential for verifying that parameter budget is allocated as intended.
|
| 177 |
+
|
| 178 |
+
---
|
| 179 |
+
|
| 180 |
+
## File 2: `geolip_losses.py` β Losses & Regularization
|
| 181 |
+
|
| 182 |
+
Every loss function and monitoring metric, with uniform interfaces. All losses return differentiable scalar tensors. All metrics return Python floats.
|
| 183 |
+
|
| 184 |
+
### CV β Coefficient of Variation of Pentachoron Volumes
|
| 185 |
+
|
| 186 |
+
The signature geometric measurement. Samples random 5-point simplices (pentachora) from the embedding space, computes their volumes via Cayley-Menger determinants, and measures the coefficient of variation (std/mean) of those volumes.
|
| 187 |
+
|
| 188 |
+
**`cv_loss(emb, target=0.22, n_samples=64, batched=True)`**
|
| 189 |
+
|
| 190 |
+
Differentiable loss: `(CV - target)Β²`. Pushes the embedding distribution toward a target volume regularity.
|
| 191 |
+
|
| 192 |
+
**`cv_metric(emb, n_samples=200, batched=True)`**
|
| 193 |
+
|
| 194 |
+
Non-differentiable monitoring metric. Reports the raw CV value.
|
| 195 |
+
|
| 196 |
+
**`cv_multi_scale(emb, scales=(3,4,5,6,7,8), n_samples=100, batched=True)`**
|
| 197 |
+
|
| 198 |
+
CV computed at multiple simplex sizes. Healthy geometry shows CV in [0.18, 0.25] at all scales. Scale-dependent CV indicates that the embedding distribution has different structure at different resolutions.
|
| 199 |
+
|
| 200 |
+
**`cayley_menger_vol2(points)`**
|
| 201 |
+
|
| 202 |
+
Raw Cayley-Menger determinant computation. Given (B, N, D) point sets, returns (B,) squared simplex volumes. The mathematical foundation underlying all CV computation.
|
| 203 |
+
|
| 204 |
+
*Why CV exists*: The coefficient of variation of simplex volumes on S^(d-1) measures how regularly the embeddings fill the sphere. CV β 0 means all simplices have identical volume (perfectly uniform distribution). CV >> 1 means volumes vary wildly (tight clusters with voids).
|
| 205 |
+
|
| 206 |
+
*The natural basin*: Extensive experimentation (43 configurations, noise prediction with zero data structure) established that smooth optimization on S^(d-1) naturally converges to CV β 0.23 at d=128 with no CV loss applied. This is the equilibrium between the sphere's rigidity (fixed curvature) and gradient descent's smoothness (continuous updates). Setting cv_target=0.80 and applying weight=100 in a full training run still produced CV=0.20 β the sphere's geometry overrides the loss.
|
| 207 |
+
|
| 208 |
+
*The floor*: Even with extreme force (weight=100, target=0.00), CV cannot be pushed below ~0.11. This is the hard geometric floor β the maximum volume regularity achievable by smooth functions on S^(d-1).
|
| 209 |
+
|
| 210 |
+
*Batched computation*: The default `batched=True` eliminates the Python loop over samples. All n_samples pentachora are sampled simultaneously via `argsort(rand)`, all Cayley-Menger matrices are constructed in parallel, and a single `torch.linalg.det` call on shape `(n_samples, 6, 6)` computes all volumes at once. Measured speedup: **141x** at n=200 samples. The `batched=False` fallback exists for debugging and validation.
|
| 211 |
+
|
| 212 |
+
### InfoNCE
|
| 213 |
+
|
| 214 |
+
**`nce_loss(z1, z2, temperature=0.07, normalize=True)`**
|
| 215 |
+
|
| 216 |
+
Standard symmetric InfoNCE contrastive loss. Two augmented views of the same sample should produce similar embeddings; different samples should produce dissimilar ones. Returns both the loss and the accuracy (fraction of correctly matched pairs).
|
| 217 |
+
|
| 218 |
+
*Why it exists at three levels*: InfoNCE is applied to embeddings (external domain), patchwork outputs (geometric domain), and triangulations (internal domain). Each level enforces view consistency at a different stage of the pipeline. Embedding NCE ensures the encoder produces stable features. Patchwork NCE ensures the geometric interpretation is view-invariant. Triangulation NCE ensures the angular distance profile is stable across augmentations.
|
| 219 |
+
|
| 220 |
+
The temperature parameter controls sharpness: lower temperature makes the loss more sensitive to small similarity differences. The default 0.07 for embeddings is sharper than the 0.1 used for assignments, reflecting the higher precision expected at the embedding level.
|
| 221 |
+
|
| 222 |
+
### Classification
|
| 223 |
+
|
| 224 |
+
**`ce_loss(logits, targets)`** β Standard cross-entropy. Returns loss and accuracy.
|
| 225 |
+
|
| 226 |
+
**`ce_loss_paired(logits1, logits2, targets)`** β Averaged cross-entropy over two augmented views. Both views should classify correctly; averaging prevents the model from specializing on one augmentation style.
|
| 227 |
+
|
| 228 |
+
### Bridge
|
| 229 |
+
|
| 230 |
+
**`bridge_loss(bridge_logits, assign_targets, detach_targets=True)`**
|
| 231 |
+
|
| 232 |
+
The bridge forces the patchwork to understand the constellation's assignment. The patchwork receives triangulation distances and produces an interpretation. The bridge head takes that interpretation and predicts which anchor each embedding was assigned to. If the patchwork has learned to read the constellation's structure, this prediction is easy. If not, the bridge loss provides gradient that teaches it.
|
| 233 |
+
|
| 234 |
+
*Why detach*: Assignment targets are detached from the computation graph by default. This makes the bridge one-way: the constellation teaches the patchwork, but the patchwork cannot modify the constellation's assignment. Without detachment, classification gradients would flow backward through the bridge into anchor positions, defeating the separation between internal and external domains.
|
| 235 |
+
|
| 236 |
+
**`bridge_loss_paired(bridge1, bridge2, assign1, assign2)`** β Averaged over two views.
|
| 237 |
+
|
| 238 |
+
### Assignment
|
| 239 |
+
|
| 240 |
+
**`assign_bce_loss(soft_assign, cos_to_anchors)`**
|
| 241 |
+
|
| 242 |
+
Binary cross-entropy between the soft assignment (softmax over cosine similarities) and a hard one-hot target at the nearest anchor. Pushes assignments toward crispness β each embedding should clearly belong to one anchor, not be smeared across many.
|
| 243 |
+
|
| 244 |
+
*Why BCE, not CE*: The target is one-hot over A anchors (256-2048). Standard cross-entropy treats this as a classification problem and applies log-softmax, which is numerically appropriate. But the soft assignment is already a probability distribution (output of softmax), and we want to measure how close it is to a specific target distribution. BCE operates element-wise, measuring the divergence at every anchor position independently.
|
| 245 |
+
|
| 246 |
+
**`assign_nce_loss(assign1, assign2, temperature=0.1)`**
|
| 247 |
+
|
| 248 |
+
InfoNCE between assignments from two augmented views. Two views of the same image should produce the same assignment pattern. This is the internal domain's view-consistency signal.
|
| 249 |
+
|
| 250 |
+
### Attraction
|
| 251 |
+
|
| 252 |
+
**`attraction_loss(cos_to_anchors)`**
|
| 253 |
+
|
| 254 |
+
`1 - max_cos`, averaged over the batch. Pulls each embedding toward its nearest anchor. Without this force, embeddings can drift to regions of S^(d-1) far from any anchor, where triangulation distances are large and uninformative.
|
| 255 |
+
|
| 256 |
+
*Balance*: Attraction pulls embeddings toward anchors; spread pushes anchors apart. The equilibrium produces a Voronoi tessellation where each embedding is close to its designated anchor but anchors are maximally separated. Weight 0.25 in the standard configuration.
|
| 257 |
+
|
| 258 |
+
### Spread
|
| 259 |
+
|
| 260 |
+
**`spread_loss(anchors, target_cos=0.0)`**
|
| 261 |
+
|
| 262 |
+
`ReLU(cos_similarity - target_cos)`, averaged over all anchor pairs. Penalizes any pair of anchors whose cosine similarity exceeds the target (default 0.0, meaning orthogonal). Keeps anchors spread across the sphere rather than collapsing into clusters.
|
| 263 |
+
|
| 264 |
+
*Why ReLU*: Only penalizes similarity above the target. Anchors that are already orthogonal or anti-aligned receive zero gradient. This is a soft constraint, not a hard one β it permits temporary clustering during training when the task demands it, while providing a restoring force toward spread.
|
| 265 |
+
|
| 266 |
+
### kNN Accuracy
|
| 267 |
+
|
| 268 |
+
**`knn_accuracy(embeddings, targets, k=1)`**
|
| 269 |
+
|
| 270 |
+
Non-differentiable metric. Classifies each embedding by the label of its nearest neighbor (or majority vote of k neighbors) in embedding space. This validates the geometric structure independently of the task head β if kNN accuracy is high, the embedding space has learned a geometry that separates classes without requiring a learned classifier.
|
| 271 |
+
|
| 272 |
+
*Why it matters*: The gap between task head accuracy and kNN accuracy measures how much the model depends on the learned classifier versus the raw geometric structure. A small gap means the geometry is doing the work. A large gap means the task head is compensating for poor geometry.
|
| 273 |
+
|
| 274 |
+
### Three-Domain Compound Loss
|
| 275 |
+
|
| 276 |
+
**`three_domain_loss(output, targets, constellation, ...)`**
|
| 277 |
+
|
| 278 |
+
The complete cooperative loss function with all weight arguments exposed. Standalone alternative to `InternalConstellationCore.compute_loss()` for use outside the standard encoder pipeline.
|
| 279 |
+
|
| 280 |
+
Default weights:
|
| 281 |
+
```
|
| 282 |
+
EXTERNAL: CE Γ 1.0 + NCE_emb Γ 0.5
|
| 283 |
+
GEOMETRIC: NCE_pw Γ 1.0 + bridge Γ 1.0
|
| 284 |
+
INTERNAL: assign Γ 0.5 + assign_nce Γ 0.25 + NCE_tri Γ 0.5
|
| 285 |
+
+ attract Γ 0.25 + CV Γ 0.01 + spread Γ 0.01
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
These weights were tuned to give each domain approximately equal total gradient magnitude. Without explicit balancing, the internal domain (6 terms) dominates the external domain (2 terms) by raw term count.
|
| 289 |
+
|
| 290 |
+
---
|
| 291 |
+
|
| 292 |
+
## File 3: `geolip_encoder.py` β Trainable Model
|
| 293 |
+
|
| 294 |
+
The complete image classification pipeline: pixels in, logits out, with full geometric structure exposed at every stage.
|
| 295 |
+
|
| 296 |
+
### ConvEncoder
|
| 297 |
+
|
| 298 |
+
**`ConvEncoder(output_dim)`**
|
| 299 |
+
|
| 300 |
+
8-layer convolutional encoder in 4 blocks: (conv3Γ3-BN-GELU) Γ 2 + MaxPool. Channels progress 64 β 128 β 256 β 384. Final adaptive average pooling to 1Γ1, followed by a linear projection to `output_dim` with LayerNorm.
|
| 301 |
+
|
| 302 |
+
L2 normalization is intentionally NOT applied inside the encoder. The raw feature norm carries information (encoder confidence), which MagnitudeFlow uses as input. The caller applies `F.normalize()` after extracting the raw magnitude.
|
| 303 |
+
|
| 304 |
+
### InternalConstellationCore
|
| 305 |
+
|
| 306 |
+
**`InternalConstellationCore(num_classes, dim, n_anchors, n_comp, d_comp, ...)`**
|
| 307 |
+
|
| 308 |
+
The three-domain head that owns the constellation geometry. Contains:
|
| 309 |
+
|
| 310 |
+
- **Constellation**: anchors on S^(d-1), shaped by internal losses and push
|
| 311 |
+
- **Patchwork**: interprets magnitude-weighted triangulation distances
|
| 312 |
+
- **Bridge**: linear projection from patchwork to anchor space (proves patchwork understands the constellation)
|
| 313 |
+
- **Task head**: MLP reading `[soft_assignment, patchwork, embedding]` β logits
|
| 314 |
+
|
| 315 |
+
The `forward_paired()` method processes two augmented views simultaneously, returning a dict with all intermediate representations needed by every loss term. The `compute_loss()` method computes all three domains with configurable per-term weights.
|
| 316 |
+
|
| 317 |
+
*Separation principle*: The task head reads the constellation's assignment but does not shape it. CE gradient flows through the task head to the encoder (improving features) and through the patchwork (improving interpretation), but constellation anchors are shaped only by internal losses + push. This prevents the classifier from repositioning anchors as classification shortcuts.
|
| 318 |
+
|
| 319 |
+
### GeoLIPImageEncoder
|
| 320 |
+
|
| 321 |
+
**`GeoLIPImageEncoder(num_classes, output_dim, n_anchors, n_comp, d_comp, ...)`**
|
| 322 |
+
|
| 323 |
+
The full pipeline. Combines ConvEncoder + MagnitudeFlow + InternalConstellationCore into a single module with clean interfaces:
|
| 324 |
+
|
| 325 |
+
- `forward(x)` β Single-view eval: pixels β logits + all geometric outputs
|
| 326 |
+
- `forward_paired(v1, v2)` β Two-view training: paired inputs β dict for loss computation
|
| 327 |
+
- `compute_loss(output, targets, **kwargs)` β Three-domain loss with all weights exposed
|
| 328 |
+
- `make_optimizer(lr, weight_decay)` β Builds AdamW with proper anchor parameter exclusion
|
| 329 |
+
- `get_anchor_param_ids()` β Returns param IDs that must have `weight_decay=0`
|
| 330 |
+
- `summary()` β Prints parameter breakdown by submodule
|
| 331 |
+
|
| 332 |
+
*`make_optimizer()`*: Constellation anchors and relay layer anchors live on spheres. Weight decay pulls parameters toward the origin, which would collapse anchors to zero norm and destroy their geometric meaning. `make_optimizer()` automatically identifies all anchor parameters and places them in a separate param group with `weight_decay=0`.
|
| 333 |
+
|
| 334 |
+
---
|
| 335 |
+
|
| 336 |
+
## Empirical Constants
|
| 337 |
+
|
| 338 |
+
Two constants have been validated across 17+ models, all architectures and modalities:
|
| 339 |
+
|
| 340 |
+
**CV β 0.20β0.23** β The natural coefficient of variation of pentachoron volumes on S^(d-1) under smooth optimization. Observed at effective dimension ~16 across contrastive models, language models, diffusion models, and VAEs. Setting cv_target=0.80 with weight=100 still produces CV=0.20. The constant is a property of the sphere's curvature interacting with gradient descent's smoothness, not a hyperparameter.
|
| 341 |
+
|
| 342 |
+
**0.29154 radians** β The binding/separation phase boundary. Below this angular distance, an embedding is structurally bound to its nearest anchor (local geometry dominates). Above it, task pressure has moved the embedding beyond local curvature (classification dominates). Observed independently in contrastive training, language modeling, ODE flow matching, and alpha parameter convergence across architectures.
|
| 343 |
+
|
| 344 |
+
---
|
| 345 |
+
|
| 346 |
+
## Design Rules
|
| 347 |
+
|
| 348 |
+
These are not preferences β they are empirically validated constraints. Violating them produces measurable degradation.
|
| 349 |
+
|
| 350 |
+
1. **Never use attention in geometric pipelines.** Softmax averaging destroys angular structure. Each attention layer without residual halves effective dimensionality. Use ConstellationRelay or RelayLayer instead.
|
| 351 |
+
|
| 352 |
+
2. **Never use global average pooling in geometric encoders.** It collapses spatial structure. Flatten or use spatial statistics (mean+std per channel minimum). Confirmed empirically: 243-d avg pool drops accuracy from ~70% to ~29% vs 15552-d flatten.
|
| 353 |
+
|
| 354 |
+
3. **Never apply weight decay to anchor parameters.** Anchors live on S^(d-1). Decay pulls them toward the origin, destroying normalization. Use `make_optimizer()` or manually separate param groups.
|
| 355 |
+
|
| 356 |
+
4. **Never let classification gradient reach anchor positions.** Detach assignment targets in the bridge. Let push handle global anchor repositioning. CE gradient on anchors creates classification shortcuts that destroy the Voronoi structure.
|
| 357 |
+
|
| 358 |
+
5. **Always L2-normalize before triangulation.** Triangulation measures angular position. Unnormalized embeddings mix magnitude and direction, making distances meaningless.
|
| 359 |
+
|
| 360 |
+
6. **Relay gates must initialize cold.** `gate_init=-3.0` (sigmoid β 0.047). Hot initialization causes deep relay stacks to diverge before the network has learned meaningful features.
|