Spaces:
Sleeping
Sleeping
| # Data-Scaling Study β U-Net vs SegFormer-B0 | |
| Controlled experiment: **how does training-set size affect the performance of a CNN (U-Net) vs a transformer (SegFormer-B0)** on the solar-panel segmentation task? | |
| | Variable | Values | | |
| |---|---| | |
| | Architecture | U-Net, SegFormer-B0 (`nvidia/mit-b0`) | | |
| | Train data share | 25 %, 50 %, 100 % (nested: 25 β 50 β 100) | | |
| | Validation set | full `final_data/val/` (1,331 samples), constant across all 6 runs | | |
| | Image size | 128 Γ 128 | | |
| | Optimizer | Adam, lr = 1e-4 | | |
| | Scheduler | `ReduceLROnPlateau(mode='max', patience=5, factor=0.5)` on val Dice | | |
| | Loss | `0.5 Β· BCE + 0.5 Β· Dice` | | |
| | Augment | HFlip, VFlip, Rot15 | | |
| | Epochs | 50 | | |
| | Batch size | 16 | | |
| | Subset seed | 42 | | |
| The hyperparameters mirror each model's existing trainer in [pv_panel_models/](../../pv_panel_models/) so the only varied factor is **training-data volume**. | |
| ## What gets trained vs reused | |
| | Share | U-Net | SegFormer-B0 | | |
| |---|---|---| | |
| | 25 % | trained from scratch | trained from scratch | | |
| | 50 % | trained from scratch | trained from scratch | | |
| | 100 % | **reused** from `pv_panel_models/unet_model/checkpoints/best_model.pth` | **reused** from `pv_panel_models/vit_model/checkpoints/best_model.pth` | | |
| For 100% we copy the existing best checkpoints and parse the existing text logs into JSON. Because the old trainers used per-batch-averaged metrics and never logged mIoU, [bootstrap_100.py](bootstrap_100.py) does a no-grad val pass on each copied checkpoint with the new metrics code so the scaling chart at 100% has a number that's directly comparable to the 25/50% points. | |
| Only **best**-epoch checkpoints are kept (`{model}_{share}_best.pth`). No `_final.pth` is saved. | |
| --- | |
| ## Layout | |
| ``` | |
| experiments/data_scaling_study/ | |
| βββ README.md # this file | |
| βββ requirements.txt | |
| βββ subsets/ | |
| β βββ make_subsets.py # builds the three subset files (idempotent) | |
| β βββ subset_25.txt | |
| β βββ subset_50.txt | |
| β βββ subset_100.txt | |
| βββ dataset.py # SubsetSolarPanelDataset (reads filename list) | |
| βββ metrics.py # confusion-matrix-based mIoU / IoU / Dice / pixel acc | |
| βββ models.py # re-exports U-Net + SegFormer-B0 builders | |
| βββ train.py # unified trainer (--model, --share β {25,50}) | |
| βββ bootstrap_100.py # copy + parse + val-recompute for 100% point | |
| βββ run_all.sh # runs all 4 trainings + bootstrap | |
| βββ checkpoints/ # populated during training / bootstrap | |
| β βββ unet_25_best.pth unet_50_best.pth unet_100_best.pth | |
| β βββ segformer_b0_25_best.pth segformer_b0_50_best.pth segformer_b0_100_best.pth | |
| βββ logs/ # per-run JSON metric history | |
| β βββ unet_{25,50,100}.json | |
| β βββ segformer_b0_{25,50,100}.json | |
| β βββ *.stdout.log # captured stdout from run_all.sh | |
| βββ dashboard/ | |
| βββ app.py # Streamlit dashboard | |
| ``` | |
| --- | |
| ## How to run | |
| ### 0 Β· Install | |
| ```bash | |
| pip install -r requirements.txt | |
| ``` | |
| ### 1 Β· Build the subsets (once) | |
| ```bash | |
| python subsets/make_subsets.py | |
| ``` | |
| Outputs three text files; asserts `25 β 50 β 100`. Re-running is safe and reproducible (seed = 42). | |
| ### 2 Β· Train the four 25/50% runs | |
| One run at a time: | |
| ```bash | |
| python train.py --model unet --share 25 | |
| python train.py --model unet --share 50 | |
| python train.py --model segformer_b0 --share 25 | |
| python train.py --model segformer_b0 --share 50 | |
| ``` | |
| ### 3 Β· Bootstrap the 100% point | |
| ```bash | |
| python bootstrap_100.py # copy + parse + val-recompute (~30s GPU work) | |
| python bootstrap_100.py --skip-val # if you don't want to run any inference | |
| ``` | |
| ### 2+3 in one go | |
| ```bash | |
| ./run_all.sh # all 4 trainings + bootstrap | |
| ./run_all.sh unet # only U-Net 25/50% trainings | |
| ./run_all.sh segformer_b0 # only SegFormer-B0 25/50% trainings | |
| ./run_all.sh bootstrap # only the bootstrap step | |
| ``` | |
| Each training run writes: | |
| - `checkpoints/{model}_{share}_best.pth` β highest val Dice across all 50 epochs | |
| - `logs/{model}_{share}.json` β per-epoch loss / dice / iou / miou / pixel_acc | |
| Logs are written incrementally β safe to interrupt and inspect. | |
| The bootstrap step writes: | |
| - `checkpoints/{model}_100_best.pth` β copy of the existing baseline best | |
| - `logs/{model}_100.json` β parsed text-log epochs + `recomputed_val_metrics` | |
| ### 4 Β· Dashboard | |
| ```bash | |
| streamlit run dashboard/app.py | |
| ``` | |
| Three tabs: | |
| 1. **Learning curves** β every (model, share) combination, switchable metric, train/val/both. Per-epoch curves at 100% are old-definition (parsed from text log) and mIoU is omitted there. | |
| 2. **Data share vs final** β best val mIoU/Dice/IoU/PixelAcc as a function of `% train data`. All three points use the new global metric code (100% via bootstrap). | |
| 3. **Inference** β upload an image, see the 2Γ3 grid of predictions (both models Γ all shares), with a tweakable threshold. | |
| The dashboard tolerates partial state β you can run it after one training is done and watch the picture fill in. | |
| --- | |
| ## Metric definitions | |
| We accumulate TP / FP / FN / TN over the full epoch and compute: | |
| | Metric | Formula | | |
| |---|---| | |
| | `iou` (foreground) | TP / (TP + FP + FN) | | |
| | `miou` | mean of foreground-IoU and background-IoU | | |
| | `dice` | 2Β·TP / (2Β·TP + FP + FN) | | |
| | `pixel_acc` | (TP + TN) / total | | |
| This differs subtly from the existing baselines in [pv_panel_models/](../../pv_panel_models/), which average per-batch IoU. For a controlled scaling study the global formulation is the standard (and matches PASCAL/Cityscapes-style mIoU reporting). | |
| The 25/50% runs always use the new (global) formulation. The 100% per-epoch curves use the old (per-batch-averaged) formulation parsed from existing text logs β but the **scaling-chart point** at 100% is recomputed under the new formulation by `bootstrap_100.py`'s val pass. | |
| --- | |
| ## Reproducing the partition | |
| `subsets/make_subsets.py` does: | |
| 1. List all `*.jpg` files in `final_data/train/images/` (5,325 files). | |
| 2. Sort alphabetically (deterministic order). | |
| 3. Shuffle with `random.Random(42)`. | |
| 4. Take the first 25 % / 50 % / 100 % β 1,331 / 2,662 / 5,325 files. | |
| 5. Assert nesting (25 β 50 β 100). | |
| Subsets are stored as plaintext filename lists β both `train.py` and the dashboard read them, so the partition is the single source of truth. | |