grgngl's picture
Upload README.md with huggingface_hub
0772aa4 verified
|
Raw
History Blame Contribute Delete
11 kB
---
license: mit
pipeline_tag: image-segmentation
tags:
- vesuvius
- herculaneum
- fibers
- ink-detection
- computed-tomography
- 3d-segmentation
- self-distillation
- self-supervised
- volumetric-imaging
---
# PHerc. Paris 4 β€” 4-class fiber/ink segmentation, self-distilled (step 29000)
Segments **background / vertical fiber / horizontal-angular fiber / ink** β€” 4
classes, in 3D, directly in micro-CT of **PHerc. Paris 4** β€” trained with **no
fixed ground truth** via self-distillation from two frozen teacher UNets. This
is villa's [`scripts/fiber_5class`](https://github.com/ScrollPrize/villa/tree/main/scripts/fiber_5class)
pipeline (PR [#985](https://github.com/ScrollPrize/villa/pull/985)), and this
checkpoint is confirmed (via direct inspection of its embedded training config,
which matches its W&B run exactly) to be the real, finished model that
pipeline produced.
**This model's own training pipeline does not use DINO at all** β€” it is pure
two-teacher self-distillation (see below). DINO only appears earlier/elsewhere
in this broader fiber-modeling effort, in a separate checkpoint that may feed
this run's fiber teacher input β€” see **Related models**.
![debug figure](images/p4_4class_36pykwky_debug_figure_step29899.png)
*Training-time debug visualization from this run at step 29899 (image slice,
teacher probability maps, watershed instances, student prediction). Logged to
W&B, not an independent evaluation.*
## Model details
| | |
|---|---|
| Architecture | `vesuvius` `NetworkFromConfig` 3D UNet (`shared_encoder`/`shared_decoder`/`task_heads`) β€” verified directly from the checkpoint: 544 encoder tensors, 60 decoder tensors, single head |
| Output | **4 channels**, softmax, head named `task_heads.labels` β€” verified shape `(4, 32, 1, 1, 1)` |
| Classes | `0` background Β· `1` vertical fiber Β· `2` horizontal/angular fiber Β· `3` ink |
| Input | 1-channel CT, 256Β³ patches |
| This checkpoint | step **29000** of a 30000-step schedule Β· W&B run [`p4_4class_ddp8_20260526`](https://wandb.ai/vesuvius-challenge/paris4-full-features/runs/36pykwky) (`36pykwky`, project `paris4-full-features`, state **finished**) |
| Weights | `model` (raw) and `ema` (**EMA β€” recommended for inference**, decay 0.9995) |
| Optimisation | SGD + Nesterov (lr 0.005, momentum 0.99, weight_decay 3e-5), cosine LR, 2000-step warmup, bf16, CE + multiclass soft Dice (0.1 label smoothing each, Dice over foreground classes only), batch size 2 Γ— 8 GPUs (`ddp8`) |
| Trained on | PHerc. Paris 4, 2.4 Β΅m scan (`s3://vesuvius-challenge-open-data/PHercParis4/volumes/20260411134726-2.400um-0.2m-78keV-masked.zarr/`) |
We verified this checkpoint directly (`torch.load(..., weights_only=False)`):
its embedded `config.wandb_run_name` is `p4_4class_ddp8_20260526`, its
`config.out_dir` is `/ephemeral/fiber_5class_ckpts/p4_4class_ddp8_20260526` β€”
both matching the W&B run's own recorded config exactly, and `step=29000`
matches the file's provenance exactly. `save_every=1000` with
`num_iterations=30000` means step 29000 is mathematically the **last**
checkpoint this run could have saved (the loop exits at step 30000 before
another save triggers) β€” this is the final checkpoint of a finished run, not
an arbitrary snapshot.
This run was reached after two earlier attempts under the same name failed
(`5ga68dxv`) or crashed (`q8jmzbiv`), and after an even earlier, broader
experiment line tagged `5class`/`fiber-ink-papyrus` (`out_channels=5`, adding a
"papyrus" class) was simplified down to the 4-class scheme published here.
## Training procedure: two-teacher self-distillation (verified against source)
Read directly from `label_generator.py` (`FiveClassLabelGenerator`) and
`train.py` in PR #985 β€” this is a from-source description, not an inference
from config field names:
1. `fiber_prob = sigmoid(fiber_teacher(image))`, `ink_prob = sigmoid(ink_teacher(image))` β€” two independent frozen teacher UNets, FG channel only.
2. `fiber_mask = fiber_prob > fiber_thr` (0.5).
3. GPU watershed-from-minima (`cuws`) on the distance transform of `fiber_mask` (`ws_image_mode="distance"`, `ws_h_merge=14000`) β†’ per-instance fiber segmentation.
4. **Per-instance PCA** on each instance's ZYX voxel coordinates: `|principal_axis Β· αΊ‘| > pca_cos_threshold` (0.819 = cos 35Β°) β†’ class **1** (vertical), else class **2** (horizontal/angular). Instances below `ws_min_voxels` (400) default to class 2 rather than being dropped.
5. **Ink overrides fiber:** `label[ink_prob > ink_thr] = 3` (`ink_thr=0.1`) β€” applied after the fiber/orientation assignment, so ink always wins where the ink teacher is confident.
6. **Dark-voxel guard (final step):** `label[raw < dark_voxel_thr] = 0` (`dark_voxel_thr=90`) β€” forces very dark/air voxels to background regardless of any earlier assignment.
Loss = cross-entropy (label smoothing 0.1) + multiclass soft Dice (smoothing
0.1, foreground classes only). A fresh pseudo-label is generated from the two
frozen teachers **every step** β€” there is no fixed/static label set at any
point in training.
The two teacher checkpoints for this run were configured as
`/ephemeral/fiber_5class_inputs/fiber_teacher.pth` and `ink_teacher.pth` β€”
generic on-disk names that don't self-identify their origin. Per the identical
`scripts/fiber_5class/train.py` module docstring, the fiber teacher is
documented as "ihoo3tpl ckpt", i.e. very likely
[`scrollprize/fiber_dinoguided_2class_step010000`](https://huggingface.co/scrollprize/fiber_dinoguided_2class_step010000)
(not proven byte-identical β€” it was copied/renamed on the training box). The
ink teacher is a separate checkpoint we never had; it no longer exists on the
original training instance and was not found anywhere else we checked, so we
are treating it **as unrecoverable** and are not able to publish it.
## Metrics
Final logged values at step 29999 (run marked **finished**; checkpoint
published here is step 29000, the last one actually saved):
| metric | value |
|---|---|
| `loss` (ce + dice) | 1.0055 |
| `loss_ce` | 0.4513 |
| `loss_dice` | 0.5542 |
| `metrics/dice_0_bg` | 0.961 |
| `metrics/dice_1_vert_fiber` | 0.673 |
| `metrics/dice_2_horiz_fiber` | 0.705 |
| `metrics/dice_3_ink` | 0.791 |
| `metrics/dice_fg_mean` | 0.723 |
| `pseudo/frac_bg` / `frac_vert` / `frac_horiz` / `frac_ink` | 0.845 / 0.026 / 0.080 / 0.049 |
| `pseudo/n_instances_mean` | 27.5 |
| `pseudo/n_vert_mean` | 9 |
**Important:** the per-class Dice above is **student-vs-its-own-pseudo-label
self-consistency**, recomputed each `val_every` steps by re-forwarding the
student on a clean (non-augmented) training crop and comparing to that crop's
pseudo-label β€” confirmed directly from `train.py`'s logging code. It is **not**
accuracy against independent, human-verified ground truth (none exists for
this pipeline). Treat these numbers as a training-health signal, not a
benchmark score.
![debug mask](images/p4_4class_36pykwky_debug_mask_step29899.png)
*Categorical mask visualization (pseudo-label vs. student prediction) from the
same step, with the fixed class palette used throughout this pipeline.*
## Relationship to other fiber-effort models β€” please read before conflating pipelines
This is the only one of the four related repos published so far whose own
training loop is DINO-free. The others are separate, earlier, or upstream
components of the same broader effort:
- **[`scrollprize/fiber_dinoguided_2class_step010000`](https://huggingface.co/scrollprize/fiber_dinoguided_2class_step010000)** β€” 2-class (background/fiber) DINO-embedding-guided self-training checkpoint, very likely (not proven byte-identical) the `fiber_teacher` input consumed by this run. Trains completely differently (Otsu + DINO-similarity dynamic pseudo-labels, no watershed, no PCA, no ink).
- **[`scrollprize/dinovol_v2_ps8_supcon3class_step362500`](https://huggingface.co/scrollprize/dinovol_v2_ps8_supcon3class_step362500)** and **[`scrollprize/fiber_selftrain_teacher_epoch30`](https://huggingface.co/scrollprize/fiber_selftrain_teacher_epoch30)** β€” further upstream still (inputs to producing this run's likely fiber-teacher checkpoint, not direct inputs to this run itself).
- **[`scrollprize/fiber_hz_vt`](https://huggingface.co/scrollprize/fiber_hz_vt)** β€” an independent, supervised, real-annotation-trained 2-class horizontal/vertical model (villa PR [#825](https://github.com/ScrollPrize/villa/pull/825)). Different pipeline, different training data (WebKnossos skeleton traces vs. self-distillation), not directly comparable.
## Files
| File | Size | Role |
|---|---|---|
| `p4_4class_ddp8_20260526_step029000.pth` | ~2.1 GB | `model` (raw) + `ema.model_state` (recommended for inference) + `optimizer` + embedded `config`. |
| `images/p4_4class_36pykwky_debug_figure_step29899.png` | β€” | Training-time debug figure (illustrative only), from this run's W&B logs. |
| `images/p4_4class_36pykwky_debug_mask_step29899.png` | β€” | Training-time categorical mask visualization (illustrative only), from this run's W&B logs. |
## Usage
```python
import torch
from huggingface_hub import hf_hub_download
path = hf_hub_download(
"scrollprize/fiber_ink_4class_selfdistill",
"p4_4class_ddp8_20260526_step029000.pth",
)
ckpt = torch.load(path, map_location="cpu", weights_only=False)
state = ckpt["ema"]["model_state"] # recommended over ckpt["model"]
# Build with vesuvius' NetworkFromConfig (target "labels", out_channels=4,
# in_channels=1, patch_size 256^3) then load_state_dict(state).
```
The `vesuvius` package and the full training pipeline are in
<https://github.com/ScrollPrize/villa> (`scripts/fiber_5class/`).
## Links
- **Code:** <https://github.com/ScrollPrize/villa> β€” PR [#985](https://github.com/ScrollPrize/villa/pull/985) (`scripts/fiber_5class`, this model's exact training code) Β· PR [#825](https://github.com/ScrollPrize/villa/pull/825) (related cross-frame affine infra, separate pipeline)
- **W&B run:** <https://wandb.ai/vesuvius-challenge/paris4-full-features/runs/36pykwky>
- **Data:** <https://scrollprize.org/data_browser>
- **Vesuvius Challenge:** <https://scrollprize.org>
## Caveats
- Metrics are self-consistency (student vs. its own pseudo-label), not
held-out validation against independent ground truth β€” there is none in
this pipeline.
- The ink teacher checkpoint used to train this model is not published here
and is believed unrecoverable (no longer present on the original training
instance; not found elsewhere in our search).
- The fiber teacher checkpoint used to train this model is very likely
[`scrollprize/fiber_dinoguided_2class_step010000`](https://huggingface.co/scrollprize/fiber_dinoguided_2class_step010000)
based on matching documentation in the training source, but this was not
proven byte-identical (it was renamed to a generic filename on the training
box before this run consumed it).
- Trained on a single scroll (PHerc. Paris 4); generalization to other scrolls
is untested by us.
## License
MIT.