v8-in finetuned on PHerc. 1447 (LOO-w062)
An ink-detection model for PHerc. 1447 at 8.64 µm. It is v8-in finetuned for 10 epochs on the refined ink labels of two windings, w058 and w060. The third labelled winding, w062, was left out of training ("leave-one-out w062"), so predictions on w062 are held out.

Prediction of this model on winding w062 (downsampled). Full-resolution layers, geometry and
predictions: ink-8um-pherc1447-surfaces.
Quick start
pip install -r requirements.txt # torch, numpy, tifffile, opencv, huggingface_hub, safetensors
hf download YoussefMoNader/ink-8um-v8in-pherc1447-loo-w062 --local-dir loo-w062 --exclude "training/*"
hf download YoussefMoNader/ink-8um-pherc1447-surfaces --repo-type dataset --local-dir surfaces --include "w062/layers/*"
python loo-w062/predict.py --layers surfaces/w062/layers --output w062.png w062.tif
config.json sets reverse_layers: true: the PHerc. 1447 renders store layers from −11.5 to
+11.5 voxels along the surface normal, and the model reads them in reverse (23 → 0), as in
training. predict.py applies this automatically; use --no-reverse for stacks that are
already ordered the other way. Other options (--layer-start, --stride, --batch-size,
.npy output) are described in the v8-in card.
import sys; sys.path.insert(0, "loo-w062")
from ink8um import InkDetector, predict_surface
model = InkDetector.from_pretrained("YoussefMoNader/ink-8um-v8in-pherc1447-loo-w062").cuda().eval()
prob = predict_surface(model, "surfaces/w062/layers") # reverse_layers taken from config
Test run. On an RTX PRO 6000 (Blackwell), the commands above were run in a fresh environment,
downloading the model and the w062 layers from the Hub and predicting all 69,794 tiles. They
reproduce the released w062/predictions/loo_w062.png: 99.5 % of pixels are identical and the rest
differ by one grey level (max float difference 7e-4, from fp16 kernel differences). Expect about
20 min per surface on one GPU at batch 16 (~11 GB).
Finetuning recipe
| Initialisation | v8-in epoch 6 (all weights); fresh optimiser |
| Training data | PHerc. 1447 w058 + w060, 24-layer native-resolution renders, refined ink labels + certainty masks |
| Patches | 64 px tiles at stride 48, admitted only where the certainty mask covers the whole tile (2,718 tiles); unlabelled pixels inside the mask are negatives |
| Depth order | reversed (reverse_layers: true) |
| Loss | 0.5 · Dice + 0.5 · BCE (label smoothing 0.25, background pixels weighted ×25) |
| Optimiser | AdamW, weight decay 1e-6, grad-norm clip 1.0 |
| Schedule | 10 epochs, per-step cosine from 1e-5 to 1e-6 |
| Batch | micro-batch 4 × accumulation 8 = 32, one GPU, fp16 mixed precision |
| Augmentation | as v8-in (flips, rotation, blur, dropout, depth jitter) |
| Released | final epoch (epoch 9, the 10th; 850 optimiser steps); no checkpoint selection |
Reproduce with the code in training/ (downloads the surfaces and the v8-in weights from the Hub):
hf download YoussefMoNader/ink-8um-v8in-pherc1447-loo-w062 --local-dir loo-w062
pip install -r loo-w062/training/requirements.txt
python loo-w062/training/finetune_loo_w062.py --output-dir runs/loo_w062
One epoch takes ~3 min plus ~30 s of validation on an RTX PRO 6000; 32 GB of GPU memory is
enough. The original run validated on the w062 grid its
labels were drawn on. That grid is superseded by the re-flattened w062 released in the dataset, so
the script validates on the training windings by default (for monitoring only). --train /
--val select other windings, --smoke-test N runs a one-epoch check, and --wandb enables
logging. The same library trains the base model; see the v8-in repository.
Predictions released with this model
| Surface | File | Model | Held out? |
|---|---|---|---|
| w062 (re-flattened) | w062/predictions/loo_w062.png, loo_w062_16bit.tif | this model | yes |
| w058 | w058/predictions/loo_w058.png | same recipe, trained on w060 + w062 | yes |
| w060 | w060/predictions/loo_w060.png | same recipe, trained on w058 + w062 | yes |
Each winding is predicted by the fold that did not train on it. Only the LOO-w062 weights are released here. Predictions are model estimates, not ground truth.
Files
config.json, model.safetensors model config + weights (InkDetector.from_pretrained)
ink8um/ standalone model + inference code
predict.py command-line inference
training/finetune_loo_w062.py finetuning entry point
training/configs/loo_w062.json finetuning recipe
training/lib/ training library (PyTorch Lightning)
License
Code and weights: MIT. PHerc. 1447 scan data and derived renders/labels are distributed by the Vesuvius Challenge under CC BY-NC 4.0.