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

release: add docs/TRAINING.md

Browse files
Files changed (1) hide show
  1. docs/TRAINING.md +205 -0
docs/TRAINING.md ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Training
2
+
3
+ **Status tags:** `IMPLEMENTED` · `VERIFIED` · `MEASURED` · `ATTEMPTED` · `NOT RUN` · `REJECTED`.
4
+
5
+ The project trains **small modules on frozen backbones**. No backbone is fine-tuned end-to-end. Every
6
+ hyperparameter lives in `configs/base.yaml` (no magic numbers in Python), and every trained artifact
7
+ records the frozen config hash it was trained against.
8
+
9
+ ---
10
+
11
+ ## 1. Overview
12
+
13
+ | Artifact | Backbone (frozen) | Where trained | Selection signal |
14
+ |---|---|---|---|
15
+ | `router` adapter | `all-MiniLM-L6-v2` | local CPU | val (group-split) |
16
+ | `grounding` head | RemoteCLIP ViT-B/32 | local | val fraction 0.10 |
17
+ | `change` head | STANet ResNet-18 + PAM | local | val (LEVIR split) |
18
+ | `optical_sar` fusion head | CROMA-base | local, **seed sweep** | held-out test (pre-registered) |
19
+ | `change_vqa` head | over cached change features | **external GPU (Kaggle)** | val answer accuracy |
20
+ | `vlm` LoRA adapter | SmolVLM-500M-Instruct | **external GPU** | frozen 1000-question subset |
21
+
22
+ **CPU-first.** The router and every head except the VLM adapter train on CPU. The VLM LoRA adapter
23
+ requires a GPU (T4-class).
24
+
25
+ ## 2. Router adapter
26
+
27
+ A **50,822-parameter** adapter over the frozen MiniLM encoder.
28
+
29
+ | Hyperparameter | Value |
30
+ |---|---|
31
+ | epochs | 60 |
32
+ | batch size | 64 |
33
+ | learning rate | 0.001 |
34
+ | weight decay | 0.01 |
35
+ | task loss weight | 1.0 |
36
+ | modality loss weight | 0.3 |
37
+ | binary loss weight | 0.5 |
38
+ | val ratio | 0.15 |
39
+ | hard negatives to test | true |
40
+
41
+ **Finding F4-2 — the encoder is frozen, so embeddings are cached** and the adapter trains on cached
42
+ vectors. **Measured: 20 epochs / 4,096 vectors in 0.28 s on CPU.** No GPU is required.
43
+
44
+ **Finding F4-3 — splits are by GROUP** (template / hard-negative family), never by example.
45
+ Hard-negative families are placed in the **test** split so their accuracy measures generalisation
46
+ rather than memorisation.
47
+
48
+ > **Status:** the adapter is trained and shipped. Its measured number (0.965116) is **validation-only,
49
+ > ungated, n = 86**; the router **test split was NOT RUN**.
50
+
51
+ ## 3. Grounding head
52
+
53
+ A trainable head over the frozen RemoteCLIP ViT-B/32 encoder.
54
+
55
+ | Hyperparameter | Value |
56
+ |---|---|
57
+ | learning rate | 0.0001 |
58
+ | batch size | 16 |
59
+ | epochs | 20 |
60
+ | weight decay | 0.0001 |
61
+ | warmup ratio | 0.05 |
62
+ | grad clip | 1.0 |
63
+ | val fraction | 0.10 |
64
+ | save every steps | 500 |
65
+ | box loss weight | 0.5 |
66
+ | GIoU loss weight | 0.3 |
67
+ | confidence loss weight | 0.2 |
68
+ | `positive_confidence_weight` | **20.0** |
69
+
70
+ **Architecture constraint (enforced, not documented).** Per-cell feature is
71
+ `concat([patch, text, patch·text, global_pool]) = 4 × 512 = 2048`. `core/config.py` **rejects any
72
+ value other than** `4 × grounding.encoder_projected_dim` at load time, and the specialist asserts the
73
+ same 512 against the real model — because a mismatch is a *silent* shape error that torch only raises
74
+ at the similarity step, after patch features are already cached.
75
+
76
+ **Why `positive_confidence_weight = 20.0`.** Objectness BCE sees ~1 positive cell out of 49.
77
+ Unweighted, the optimum is "no object" everywhere; the weight is what stops that collapse.
78
+
79
+ **Resolution is frozen at 224.** 448 was evaluated and **REJECTED** (paired test: mean diff −0.0147,
80
+ 95 % CI [−0.0160, −0.0134], t = −22.63, at 1.59× latency).
81
+
82
+ ## 4. Change head
83
+
84
+ STANet-style Siamese detector.
85
+
86
+ | Hyperparameter | Value |
87
+ |---|---|
88
+ | encoder | ResNet-18 |
89
+ | self-attention | **PAM** (BAM alternative not used) |
90
+ | tile size | 256 |
91
+ | tile overlap | 0 |
92
+ | threshold | 0.50 |
93
+ | min component pixels | 32 |
94
+ | learning rate | 0.001 |
95
+ | batch size | 8 |
96
+ | BCE weight | 0.5 |
97
+ | Dice weight | 0.5 |
98
+
99
+ Trained on LEVIR-CD-256 (train 7120 / val 1024 / test 2048). **This is the only task with a
100
+ `VERIFIED` headline metric** (pooled IoU 0.8122 on the immutable test split).
101
+
102
+ ## 5. Optical-SAR fusion head
103
+
104
+ | Hyperparameter | Value |
105
+ |---|---|
106
+ | input dim | 2318 = 3 × 768 + 12 + 2 |
107
+ | hidden dim | 512 |
108
+ | dropout | 0.2 |
109
+ | num classes | 19 (BigEarthNet CLC) |
110
+
111
+ **Seed sweep.** Training was run as two arms (**armA**, **armB**) × five seeds (**100–104**), with a
112
+ per-arm variance report (`armA_seed_variance_report.json`). The **production head** is a distinct,
113
+ frozen artifact (`fusion_head_production_v001/head.pt`) with its own
114
+ `production_head_record.json` and a `phase12_rerun_verification.json`.
115
+
116
+ **Pre-registration.** The headline metric is a **pre-registered** 115-class protocol
117
+ (`pre_registered_11.5`) computed over the 19-class label space on the held-out test split. The
118
+ metric JSON records `is_deciding_statistic: False`, i.e. it is a reported measurement, not a
119
+ decision statistic.
120
+
121
+ **Feature caches.** Training consumes cached CROMA features
122
+ (`fusion_features/`, `fusion_features_armB/`, ~231 MB each). These caches are **reproducible** and are
123
+ not released as model weights.
124
+
125
+ ## 6. Change-VQA head — trained externally
126
+
127
+ The change-VQA head was trained **outside this repository**, on an external GPU (Kaggle), following
128
+ `docs/R02_KAGGLE_TRAINING_GUIDE.md`. That guide's status on entry was
129
+ `IMPLEMENTATION_READY_FOR_EXTERNAL_TRAINING`, and its explicit contract is:
130
+
131
+ > **Training produces an artifact, not a verified capability, and the run record says
132
+ > `TRAINED_UNVERIFIED`.**
133
+
134
+ The returned checkpoint was **promoted** through a byte-identity gate
135
+ (`artifacts/change_vqa/run/PROMOTION.json`):
136
+
137
+ | Property | Value |
138
+ |---|---|
139
+ | sha256 | `cfae5e43b97ca930f568dc5b8ae4f36b24e9ff717af226159802206ffd63a82a` |
140
+ | bytes | 5,822,809 |
141
+ | architecture | `change_vqa_head_v1` |
142
+ | parameters | 1,453,912 |
143
+ | non-finite tensors | **0** |
144
+ | weights modified during promotion | **false** |
145
+ | byte-identical to source | **true** |
146
+ | hash agrees across | `model_metadata.json`, `run_record.json`, `hashes.json` |
147
+
148
+ **Selection:** epoch **8**, chosen on **Val answer accuracy = 0.700018**, stopped by early stopping.
149
+ Seed 42. It trains on **cached change + text features** (specs `change_feat_v1`,
150
+ `change_cache_spec c801326f85a185f8`, `text_cache_spec d2801ea1a314354a`), not on raw imagery.
151
+
152
+ > **Note.** The raw CDVQA loader (see [`DATASETS.md`](DATASETS.md)) loads examples but has **no
153
+ > training loop**; the shipped head is a cached-feature model. These are different paths and are not
154
+ > conflated.
155
+
156
+ ## 7. VLM LoRA adapter — trained externally
157
+
158
+ A PEFT LoRA adapter on **frozen** `HuggingFaceTB/SmolVLM-500M-Instruct`.
159
+
160
+ | Hyperparameter | Value |
161
+ |---|---|
162
+ | PEFT version | **0.19.1** |
163
+ | `r` (rank) | 16 |
164
+ | `alpha` | 32 |
165
+ | `dropout` | 0.05 |
166
+ | target modules | `model.text_model.*.{q,k,v,o,gate,up,down}_proj` |
167
+ | precision | **fp16** (finding C-6: T4 is SM 7.5 → **fp16, NOT bf16**) |
168
+ | batch size | 2 |
169
+ | gradient accumulation | 8 |
170
+ | learning rate | 0.0002 |
171
+ | epochs | 1 |
172
+ | gradient checkpointing | true |
173
+ | save every steps | 500 |
174
+
175
+ **Finding F5-2 (cost).** The processor's default `longest_edge` is 2048, which upscales 512-px tiles
176
+ 4× and then splits them into **17 sub-images** (`pixel_values (1, 17, 3, 512, 512)`, 1142 prompt
177
+ tokens). Pinning `processor_longest_edge: 512` yields `pixel_values (1, 1, 3, 512, 512)`. The plan
178
+ estimated a 4× cost overrun; the **measured** figure is ~17×.
179
+
180
+ **Finding F5-3.** SmolVLM requires one `<image>` token per image in the prompt; hand-written prompt
181
+ strings raise `ValueError`. Prompts are always built through `processor.apply_chat_template()`.
182
+
183
+ **Outcome:** metrics usable (exact_match 0.963, F1 0.96432, +49.5 pp) but the artifact is
184
+ **ACCEPTANCE-REJECTED** for promotion. The deployed caption/VQA path uses the **unadapted** model.
185
+ See [`MODELS.md`](MODELS.md) §3.6.
186
+
187
+ ## 8. Reproducibility contract for training
188
+
189
+ - **Seed 42** everywhere (`project.seed`).
190
+ - **Precision `fp16`** (T4 constraint), never bf16.
191
+ - Every artifact records the **frozen config hash** `78f1e3700da15aa1`; a config edit moves the hash
192
+ and invalidates the artifact.
193
+ - `save_every_steps: 500`; checkpoints are archived as provenance, not released.
194
+ - Training guides state their own entry status and **never** claim a trained artifact is a verified
195
+ capability.
196
+
197
+ ## 9. What was NOT trained
198
+
199
+ | Item | State |
200
+ |---|---|
201
+ | Backbone fine-tuning (any) | **NOT DONE** — all backbones frozen |
202
+ | Router on the test split | **NOT RUN** |
203
+ | Any end-to-end / joint training | **NOT RUN** |
204
+ | Re-training of the change head at a second resolution | **NOT RUN** |
205
+ | Benchmark adapters | **NOT RUN** |