thundercode commited on
Commit
848485b
·
verified ·
1 Parent(s): ddc0680

release: add docs/RESEARCH_NOTES.md

Browse files
Files changed (1) hide show
  1. docs/RESEARCH_NOTES.md +204 -0
docs/RESEARCH_NOTES.md ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Research Notes
2
+
3
+ Engineering findings, negative results and design decisions that would otherwise be lost. Each was
4
+ learned by **probe or execution**, not by assumption, and each is recorded so it is not rediscovered.
5
+
6
+ **Status tags:** `MEASURED` · `RESOLVED` · `REJECTED` · `OPEN` · `ATTEMPTED`.
7
+
8
+ ---
9
+
10
+ ## 1. Findings that changed the code
11
+
12
+ ### F4-1 — the MiniLM tokenizer ceiling (`MEASURED`)
13
+
14
+ The MiniLM tokenizer's own ceiling is **256** (verified by probe). The project truncates to **128** —
15
+ a deliberate truncation *well inside* the ceiling, not the model limit. Satellite queries are short;
16
+ halving the sequence halves attention cost for no measurable accuracy loss. The encoder **asserts**
17
+ `max_length ≤ 256`, because truncating above the ceiling is a silent no-op.
18
+
19
+ ### F4-2 — the router needs no GPU (`MEASURED`)
20
+
21
+ The encoder is frozen, so embeddings are **cached** and the 50,822-parameter adapter trains on cached
22
+ vectors. **Measured on CPU: 20 epochs / 4,096 vectors in 0.28 s.** No GPU required.
23
+
24
+ ### F4-3 — splits must be by group (`MEASURED`)
25
+
26
+ Splits are by **group** (template / hard-negative family), never by example. Hard-negative families
27
+ are placed in the **test** split so their accuracy measures generalisation rather than memorisation.
28
+ Splitting by example would leak template variants across the boundary.
29
+
30
+ ### F5-1 — `AutoModelForVision2Seq` does not exist (`MEASURED`)
31
+
32
+ In transformers 5.17.0, `AutoModelForVision2Seq` **does not exist** (not merely deprecated);
33
+ `AutoModelForImageTextToText` is present. The loader is resolved by **feature detection**, never
34
+ hardcoded.
35
+
36
+ ### F5-2 — the processor cost overrun is ~17×, not 4× (`MEASURED`)
37
+
38
+ The processor's default `longest_edge` is **2048**, which upscales 512-px tiles **4×** and then splits
39
+ them (`do_image_splitting=True`) into **17 sub-images**:
40
+
41
+ | Setting | `pixel_values` | prompt tokens |
42
+ |---|---|---|
43
+ | default | `(1, 17, 3, 512, 512)` | 1142 |
44
+ | pinned (`processor_longest_edge: 512`) | `(1, 1, 3, 512, 512)` | — |
45
+
46
+ The plan estimated a 4× cost overrun; the real figure is **~17×**. The value **must** be set
47
+ explicitly on the processor at construction time.
48
+
49
+ ### F5-3 — prompts must go through the chat template (`MEASURED`)
50
+
51
+ SmolVLM requires one `<image>` token per image in the prompt. Hand-written prompt strings raise
52
+ `ValueError`. Prompts are always built through `processor.apply_chat_template()`.
53
+
54
+ ### P7-1 — the RemoteCLIP projected dimension is 512 (`MEASURED`)
55
+
56
+ The RemoteCLIP ViT-B/32 transformer width is **768**, but `visual.proj` maps to a **projected** dim of
57
+ **512**. The grounding head's per-cell feature is `4 × 512 = 2048`, declared in config so
58
+ `core/config.py` can validate the head **without importing torch**.
59
+
60
+ ### C-1 — the availability mask is consumed by the head, not by CROMA (`MEASURED`)
61
+
62
+ CROMA always sees the canonical channel counts (12 optical, 2 SAR). The availability mask is applied
63
+ by the **fusion head** (`input_dim = 3 × 768 + 12 + 2 = 2318`), not by the encoder.
64
+
65
+ ### C-6 — T4 is SM 7.5, so training uses fp16, not bf16 (`MEASURED`)
66
+
67
+ The training precision is **fp16** because the target GPU (T4) is compute capability 7.5. bf16 is
68
+ not available there.
69
+
70
+ ### C-7 — `image_resolution % 8 == 0` (`MEASURED`)
71
+
72
+ CROMA requires `image_resolution % 8 == 0`. The native value **120** yields 225 patches.
73
+
74
+ ### C-8 — ZeroGPU does not support `torch.compile` (`MEASURED`)
75
+
76
+ `torch.compile` must never be enabled on the (historical) ZeroGPU target.
77
+
78
+ ### C-9 — STANet change hyperparameters are upstream-verified (`MEASURED`)
79
+
80
+ Change detection uses STANet-style architecture with upstream-verified hyperparameters (PAM
81
+ self-attention, ResNet-18 encoder).
82
+
83
+ ---
84
+
85
+ ## 2. The grounding resolution decision — a pre-registered rejection (`REJECTED`)
86
+
87
+ **Question:** should grounding decode at 448 or 224?
88
+
89
+ **Answer: 224. 448 was rejected** — and the rejection is notable because it was *pre-registered* and
90
+ then *confirmed* by a paired test:
91
+
92
+ | Comparison (over identical samples, n = 16,159) | 448 vs 224 |
93
+ |---|---|
94
+ | mean best IoU | **−0.0147** |
95
+ | recall@0.5 | −0.0022 |
96
+ | recall@0.10 | −0.0699 |
97
+ | recall@0.25 | −0.0243 |
98
+ | latency | **1.59×** |
99
+ | paired 95 % CI | [−0.0160, −0.0134] |
100
+ | paired t | **−22.63** |
101
+ | 448 better on | 8.5 % of records |
102
+ | 448 worse on | **20.9 %** of records |
103
+
104
+ 448 lost on **every** axis. The pre-registered decision rule and the paired test **agree** on 224.
105
+ This is a model of how a resolution decision should be made: declared in advance, then tested.
106
+
107
+ ---
108
+
109
+ ## 3. The router defect — a real bug, found and fixed
110
+
111
+ ### 3.1 Symptom
112
+
113
+ The query *"Where are the built-up areas in this image?"* collapsed to **`vqa`** and answered
114
+ **"River"** — instead of routing to `grounding`. A second query, *"Where is the new airport?"*,
115
+ behaved the same way.
116
+
117
+ ### 3.2 Root cause
118
+
119
+ Two functions with different information:
120
+
121
+ - **`interpret()`** — produces the console's *reading*; **asset-count-blind** (text only).
122
+ - **`chooseTask()`** — performs *dispatch*; **asset-count-aware**.
123
+
124
+ The defect was in the dispatch path's handling of spatial/lexical cues, so region queries fell
125
+ through to the generic VQA specialist. See [`ARCHITECTURE.md`](ARCHITECTURE.md) §4.
126
+
127
+ ### 3.3 Fix and verification (`RESOLVED`)
128
+
129
+ The fix was deployed to `SatQuery-Frontend` and validated by **three independent live passes**:
130
+
131
+ | Pass | Deployed HEAD | Result |
132
+ |---|---|---|
133
+ | 1 | `ff46eba42b18` + `d413d3672311` | 8/8 |
134
+ | 2 | `2d7ae53b482d` | 8/8 |
135
+ | 3 | `2d7ae53b482d` | 8/8 |
136
+
137
+ Both defect queries now dispatch to `grounding`:
138
+
139
+ | Query | Run id | Dispatched |
140
+ |---|---|---|
141
+ | Where are the built-up areas in this image? | `run_467ffa406f22` | `grounding` |
142
+ | Where is the new airport? | `run_46980ba55c62` | `grounding` |
143
+
144
+ 24 live runs, 24 correct dispatches, **0 mock nodes**. Screenshots are in
145
+ [`../screenshots/`](../screenshots/).
146
+
147
+ ---
148
+
149
+ ## 4. The harness false-positive — caught before it could lie
150
+
151
+ An earlier live-validation harness typed queries with **synthetic CDP key events**, which Chrome
152
+ **silently drops when the window lacks OS focus**. The harness therefore dispatched the page's
153
+ *default* query and still recorded a "result" — a **false pass**.
154
+
155
+ **Fix:** the current harness **asserts form state before dispatch** (`q_ok`, `obs_ok`, `t0_ok`), and
156
+ uses deterministic query entry (`js()` value-set + `type_text()` via CDP `Input.insertText`).
157
+
158
+ **Independent check:** the earlier 8/8 run was re-examined and confirmed **not** infected — its
159
+ answers were query-specific and the query text was embedded in the answers. The failure mode is
160
+ recorded because it is exactly the silent false-positive an evaluation harness must never have.
161
+
162
+ ---
163
+
164
+ ## 5. The `transport_mode: auto` fallthrough (`OPEN`)
165
+
166
+ `SATQUERY_TRANSPORT=auto` tries the tunnel, then falls through to the forward path on timeout. The
167
+ forward path to a **private** repo returns `302` quickly, but the wake step still consumes
168
+ `SATQUERY_WAKE_TIMEOUT_S` (120 s) first — so a worst-case failed request takes ≈ 249 s
169
+ (150 + 120). This is the root shape of the observed transient tunnel gap. Recorded as **OPEN**; a
170
+ deployed fix for the `codespace_name` newline on the wake path was authored separately.
171
+
172
+ ---
173
+
174
+ ## 6. The `interpret()` / `chooseTask()` asymmetry — intentional (`RESOLVED`)
175
+
176
+ For *"What changed between the earlier and later image?"* with **one** asset attached, the console
177
+ **reads** `change` while dispatch correctly falls back to **`change_vqa`**. This is not a bug: the
178
+ reading describes the question's intent, the dispatch respects what can actually be computed with the
179
+ assets present. It is documented so it is not mistaken for a defect.
180
+
181
+ ---
182
+
183
+ ## 7. Environment findings (would otherwise cost hours)
184
+
185
+ | Finding | Detail |
186
+ |---|---|
187
+ | **Dead proxy in the sandbox** | outbound calls need `--noproxy '*'` (curl) or `ProxyHandler({})` (Python). |
188
+ | **pytest is only in the repo venv** | `.venv/Scripts/python.exe`; a bare `pytest` misses it. |
189
+ | **Full-suite pytest trips a bulk-delete guard** | sandbox-specific; affects `test_safe_delete_shim`. |
190
+ | **Cloudflare `_headers` concatenate** | two matching rules are merged, not overridden; Chromium takes the **first** `max-age`. |
191
+ | **Cloudflare 308-redirects `X.html` → `/X`** | reference the extensionless path. |
192
+ | **A forwarded Codespace port returns `302`** for a private repo | this is *why* the tunnel exists. |
193
+ | **Chrome drops synthetic CDP key events without OS focus** | the harness false-positive (§4). |
194
+ | **`browser-use` block-buffers stdout** even when redirected | needs explicit line buffering to stream. |
195
+
196
+ ---
197
+
198
+ ## 8. The BigEarthNet format contradiction (`ATTEMPTED`, reported not resolved)
199
+
200
+ The BigEarthNet data format **contradicts the original plan**. This was **reported rather than
201
+ silently patched**, because quietly changing the preprocessing would move the frozen config hash. The
202
+ BigEarthNet documentation does not specify a percentile stretch; this project nevertheless applies
203
+ percentile normalisation (2/98) to match the CROMA contract. That is a **deliberate, documented
204
+ choice**, not an upstream fact. See [`DATASETS.md`](DATASETS.md) §5.3.