Herculaneum legibility proxy (proxy_v4 / proxy_s2s3_v1)

A small ResNet-18 binary classifier that scores ~1 cm windows of Vesuvius Challenge ink-probability maps for legible-text likelihood: does this window contain connected Greek letterforms, or just fiber texture / noise / damage?

It is a triage tool, not an ink detector. It runs downstream of the official ink-detection models, on their output maps, and answers a different question: "of all this detected ink, where should a human look first?"

Built and used to produce a full legibility index of Scroll 1 (PHerc. Paris 4): 125,298 windows scored across all 78 of Scroll 1's currently-published official ink maps (complete coverage, verified live against the S3 bucket 2026-07-08 β€” see "Coverage" below for how this grew from an initial 16-panel pass). 5,678 high-confidence text windows on the deduplicated wrap-series union (w010–w129); classic-lineage segments add 2,460 more on 18 physical surfaces (kept separate β€” their overlap with the series is unresolved), and 7,248 further gold windows come from redundant re-renders, counted only as consistency checks (see Coverage below). The same checkpoint, unmodified, was then run on three more scrolls with no fine-tuning in between:

Scroll Windows scored Segments/maps used Coverage of official maps Gold (β‰₯0.9) Independent check
Scroll 1 (PHerc. Paris 4) 125,298 78 official segments (16 original panels + 62 added 2026-07-08) 78 of 78 β€” complete 5,678 on the deduplicated wrap-series union w010–w129 (+2,460 on classic-lineage segments, kept separate; +7,248 redundant re-renders β€” see Coverage) 3 held-out panels, AUROC 0.985; wrap w028-037 independently gold-confirmed in 2 unrelated renders (50.9% and 40.0% gold density)
PHerc 0139 4,905 38 38 of 38 β€” complete 63 title segment ranked 2nd of 38 by gold density; 63/63 human-reviewed (single reviewer): 54 clear-text / 9 possible / 0 rejected
Scroll 5 (PHerc 172) 24,528 53 Γ— 2 ink-detector models 53 of 53 β€” complete 3 (by july_retreat-model score; 0 by the more conservative november19 model β€” see cross-render caveat below) max score 0.938 on a window with plainly visible, large uppercase Greek letters (segment w066)
Scroll 4 (PHerc 1667) 4,761 19 official maps 19 of 19 segments that carry an official ink map (1 of the 20 public segments has none) β€” complete 430 per-wrap gold fraction correlates at Spearman r = 0.843 (p < 0.0001, n = 18) with the count of Greek letters already transcribed in that wrap β€” ground truth external to this model

Coverage β€” how the Scroll 1 index reached 78/78

The index originally covered only 16 of Scroll 1's 81 public segments (one continuous panel series, w010–w100, uploaded 2026-06-23) β€” 29,853 windows, 4,491 gold. A live S3 check on 2026-07-08 found the scroll had grown to 78 populated official maps (3 of the 81 public segments still have none). The remaining 62 were scored with the identical unmodified pipeline (same proxy_v4.pt, same WIN=512/STRIDE=256 grid, same ds8-resolution preprocessing β€” verified byte-for-byte SHA256-identical against the official pre-made ink-detection/downsampled/*-ds8.jpg product before trusting it on new segments), split into four groups:

Group Segments Windows Gold (β‰₯0.9) Territory
23 "classic"-lineage panels (name-level dedup: 18 physical surfaces) 23 8,978 2,684 (2,460 on representatives + 224 on re-renders) separate bucket β€” overlap with the panel series unresolved, so never summed into the wrap-series denominator
11 more panels from the same June-2026 series, w101–w129 11 14,998 1,056 distinct β€” extends past the original w100 boundary
2 alternate-processing panels, w046-052_jordi / w053-058_jordi 2 3,929 739 overlaps β€” re-renders of w046-052/w053-058, already in the original 16 panels (confirmed by near-identical gold-density-by-row fingerprint against the originals)
26 panels from a second complete series uploaded 2026-07-01, covering w010–w129 26 67,540 6,416 one panel is unique territory: the June w104-106 segment has no published ink map; the July w104-106 panel (2,648 windows, 131 gold) covers that range and joins the deduplicated union. The other 25 panels (6,285 gold) re-cover indexed wraps and stay redundant

Correction (2026-07-08, caught in adversarial review): the _jordi panels were originally folded into the "distinct" count because their segment IDs looked like part of the w101–w129 extension. They aren't β€” they cover wrap ranges already present in the original 16-panel index. Moved to the overlap bucket; the headline distinct-territory number dropped from an incorrect 8,970 to a correct 8,231 as a result. The July-2026 series is confirmed not a byte-identical duplicate of the June one (e.g. the w010-027 file is 682 MB in June vs 1.07 GB in July, same recipe tag), but it covers the same physical wraps, so β€” like the _jordi panels β€” its gold windows are reported separately rather than folded into the "distinct territory" total, to avoid double-counting the same underlying text. [Update, 2026-07-11: with one exception found later β€” the July w104-106 panel is unique territory; see the correction below.] Both overlap groups are useful anyway as independent consistency checks: wrap range w028-037 (June vs July) scores gold at 50.9%/40.0%, and w046-052/w053-058 (original vs _jordi) show matching gold-density fingerprints band-by-band β€” unrelated renders of the same regions agreeing on where the text is.

Correction (2026-07-11, peer review): an earlier version of this card reported "8,231 gold on distinct territory" by summing the wrap-series panels with the classic segments. Two problems, both fixed here: the June w104-106 segment has no published ink map, so the July panel covering those wraps is unique territory (131 gold) wrongly excluded as redundant; and the classic bucket contained re-renders of the same physical surfaces (a _copy, a _v14, a _v2_flatboi, a _v8, an offset-0 re-render β€” 23 panels are 18 surfaces) and its physical overlap with the panel series is unresolved. Canonical accounting: deduplicated wrap-series union (June w010–w129 + July w104-106) = 5,678 gold windows; classic-lineage representatives = 2,460 gold on 18 surfaces, reported separately; redundant re-renders = 7,248 gold (224 classic + 739 _jordi + 6,285 July), used only as consistency checks. Cross-check invariant: 5,678 + 2,460 + 224 + 739 + 6,285 = 15,386 gold rows in the full 125,298-window index β€” shipped as full_index_complete.json in the companion dataset repo (SHA256 4d393d70ce886ed62b7e73e365f1d01cbe7f6efa37168fb3f27ade2b89d6e7a8; every bucket and denominator above is recomputed with asserts by its summarize_index.py). Note the score key in that file is named v3 for legacy reasons; every score in it was produced by proxy_v4.pt.

Files

File What it is MD5 (first 12)
proxy_v4.pt main checkpoint, trained on official Scroll 1 panel maps c869ce189f2a
proxy_s2s3_v1.pt domain-calibrated variant: same S1 knowledge + 192 windows of self-rendered Scroll 2/3 maps as explicit negatives 95b6839c58c8

Both are plain state_dicts for torchvision.models.resnet18() with fc = nn.Linear(512, 1) (122 tensors).

Which one to use β€” the split is by rendering pipeline, not by scroll

proxy_v4 generalizes across scrolls, as long as the map came out of the official/community rendering pipeline (the texture statistics of an "official-style" ink map are consistent regardless of which scroll it's of). See the table above for the four-scroll validation: one native, one blind positive control (0139), one cross-render-calibration caveat that's about the ink detector, not this classifier (Scroll 5), and one check against fully independent, non-visual ground truth β€” transcribed-letter counts, not just human eyeballing (Scroll 4; project-internal validation note β€” the correlation, n, and p reported above are the complete result).

proxy_s2s3_v1 exists because that assumption breaks on maps rendered by a custom / non-official pipeline β€” which for us happened to be Scrolls 2 and 3, only because no official ink maps existed yet for the regions we needed and we rendered our own. On those self-rendered mosaics, proxy_v4 alone gives an inflated background-noise floor (~0.04); the 192 self-rendered-domain negatives used to fine-tune proxy_s2s3_v1 drop that to ~0.003 without hurting Scroll 1 performance (AUROC 0.984 vs 0.985). It was never evaluated on official maps of other scrolls (0139, Scroll 5) β€” there's no reason to prefer it over proxy_v4 there, since proxy_v4 already has evidence of working on that domain and proxy_s2s3_v1 doesn't.

Map came from Use
Official / community rendering pipeline (any scroll) proxy_v4.pt
Your own custom rendering pipeline proxy_s2s3_v1.pt, or better: fine-tune your own domain-negatives the same way (see Training summary)

How to use

The preprocessing is part of the model. Deviate from it and the scores shift silently β€” this exact recipe is what the training used:

import numpy as np
import torch, torch.nn as nn, torchvision
from PIL import Image

m = torchvision.models.resnet18()
m.fc = nn.Linear(512, 1)
m.load_state_dict(torch.load("proxy_v4.pt", map_location="cpu"))
m.eval()

def score_window(win_u8):          # win_u8: 2D uint8 crop of an ink map
    c = win_u8.astype(np.float32)
    active = c[c > 10]
    if len(active) > 50:           # percentile stretch over active pixels
        lo, hi = np.percentile(active, [2, 99.5])
        c = np.clip((c - lo) / max(hi - lo, 1e-6), 0, 1)
    else:
        c = c / 255.0
    img = Image.fromarray((c * 255).astype(np.uint8)).resize((160, 160), Image.BILINEAR)
    t = torch.from_numpy(np.array(img, np.float32) / 255.0)[None].repeat(3, 1, 1)
    with torch.no_grad():
        return torch.sigmoid(m(t[None])).item()

The window must be ~1 cm physical β€” this is the #1 usage error

The model was trained on 512 px windows of ds8 panels at β‰ˆ19.2 Β΅m/px β‰ˆ 9,830 Β΅m β‰ˆ 1 cm per window. On any other map, compute the window size from the segment's real physical scale, e.g.:

px_um = (area_cm2 * 1e8 / (H * W)) ** 0.5   # from the mesh meta.json area
WIN   = round(9830 / px_um)

Two failure modes we hit ourselves, so you don't have to:

  1. Do not assume 512 px. On a 7.91 Β΅m-scan ds8 map the right window is ~155 px; on a 2.4 Β΅m-scan ds8 map it's ~512–560 px.
  2. Do not stretch undersized crops. If a fragment is smaller than WIN, resizing what you have up to 160Γ—160 changes the fiber texture's apparent frequency and inflates scores (we measured score anti-correlating with true window size, r β‰ˆ βˆ’0.45, before fixing this). Pad onto a black 160Γ—160 canvas at the training scale (61.4 Β΅m per classifier pixel) instead, and treat such windows with extra suspicion.

Thresholds we used

  • β‰₯ 0.9 β€” "gold": on Scroll 1, round-2 human review confirmed 120/120 of the model's top-ranked proposals. On PHerc 0139, all 63 gold windows were human-reviewed (single reviewer): 54 clear text (85.7%), 9 possible, 0 rejected as noise/artifact. These are one reviewer's visual verdicts on model-selected windows, not population precision estimates.
  • 0.35–0.70 β€” genuinely ambiguous band; useful for uncertainty sampling if you're fine-tuning.
  • Scores are not calibrated probabilities, and they do not transfer across ink-model renders: on Scroll 5, the same three candidate windows (segment w066 and neighbors) scored 0.938 / 0.914 / 0.903 on july_retreat-model maps but 0.266 / 0.029 / 0.334 on november19-model maps of the identical windows β€” pixel correlation between the two maps in those windows is 0.86–0.89 (i.e. both detectors render essentially the same visible letterforms; only the score disagrees). Rank within one map family; never compare raw scores across families, and never take an AND-style agreement filter (min(scoreA, scoreB) β‰₯ 0.9) at face value without checking whether one model is just systematically deflated β€” we lost 3 true positives to exactly that before catching it.

Training summary

  • Architecture: torchvision ResNet-18 (ImageNet init), fc β†’ 1, BCE-with-logits, AdamW.
  • Data (proxy_v4): 541 positive windows from three rounds of human-in-the-loop review of official Scroll 1 panel maps (393 browse-all
    • 120 model-proposed/human-confirmed + 28 uncertainty-sampled), negatives from 50 GPU-verified fiber windows (shipped in the companion dataset repo as fiber_negatives_50.jsonl; 12 of the 50 fall in the validation panels), background sampling, and 87 human-implied negatives at weight 0.7; 22 human-"unsure" windows excluded. Spatial validation: 3 full held-out panels β†’ AUROC 0.985. (Corrected 2026-07-08: the browse-all round produced 398 raw rows, but 5 of those were tagged "unsure," not positive β€” 393 + 120 + 28 = 541. The full, independently re-verified breakdown ships as training data, not just this prose summary β€” see the companion dataset repo: https://huggingface.co/datasets/LimeGS/herculaneum-legibility-proxy-labels.)
  • proxy_s2s3_v1: initialized from proxy_v4, fine-tuned at LR 1e-4 with self-rendered Scroll 2/3 negatives: 23 human-reviewed noise windows at weight 1.0, plus ~169 additional implicit negatives at weight 0.7 from the same rendering pass. The 7 human-flagged candidate windows were excluded from training entirely. Reproducibility caveat: only the 23 explicitly-reviewed windows are traceable in this release β€” the ~169 implicit negatives were read at training time from a session-scratchpad file that no longer exists, so they can't currently be regenerated bit-for-bit. This doesn't change what was trained (the weights are final), it means the companion dataset repo can reproduce 23 of the ~192 S2/S3 negatives, not all of them β€” noted there in full, not glossed over here.
  • Trains in minutes on a laptop (Apple MPS, CUDA, or CPU); this is deliberately a small, reproducible model.

Training data release

The human-labeled training coordinates (not images β€” just {panel, y, x, label, weight}, since the underlying maps are already public) are released alongside this model at https://huggingface.co/datasets/LimeGS/herculaneum-legibility-proxy-labels, with a crop-regeneration script, a consolidated training script, pinned dependency versions, and a self-test. Scope of reproducibility, stated precisely: the full proxy_v4 recipe (labels + 50 fiber negatives + procedural background negatives β†’ crops β†’ training with the original spatial holdout) can be re-run end to end from public data; expect a checkpoint that matches in architecture and closely in behavior, not bit-for-bit (GPU/RNG/environment nondeterminism; a retrained checkpoint's AUROC will vary around 0.985, while the PUBLISHED checkpoint's validation AUROC is exactly reproducible: the dataset repo's eval_checkpoint.py deterministically rebuilds the held-out split, 81 positives / 123 negatives, and yields 0.9853457794). proxy_s2s3_v1 additionally has the documented 169-negative gap. The scoring INDEX built with the model ships in the same dataset repo (full_index_complete.json + summarize_index.py); the sweep script that produced it is project-internal, but its recipe is fully specified above (the score_window() preprocessing on a WIN=512 / stride-256 grid).

Limitations / what this does not claim

  • Not an ink detector. Input is an ink-probability map produced by the official/community detectors, never raw CT.
  • Finds letterform-like structure, not meaning. A high score means "connected strokes at letter scale," which is what a human reviewer should look at β€” it is not a transcription and not proof of novel text.
  • Domain is defined by rendering pipeline, not by scroll identity β€” see "Which one to use" above. proxy_v4 is validated on official-style maps from four scrolls (S1, PHerc 0139, Scroll 5, Scroll 4); a fifth scroll's official maps haven't been tried, though the pattern across four independent scrolls (including one quantitative correlation against transcribed-text ground truth, not just spot checks) makes transfer the expected outcome, not a hopeful one.
  • Cross-render score shifts are real even within proxy_v4's validated domain: two different official ink-detection models of the same scroll region can disagree by 0.1+ (see Thresholds) β€” that's the ink detector's output changing, not this classifier's domain breaking.
  • Windows flagged "gold" on public maps are locations worth expert attention, on data anyone can download β€” whether the text there is actually untranscribed must be checked against the papyrological literature, which is a moving target.

Provenance & license

Trained exclusively on publicly released Vesuvius Challenge data (CC BY-NC 4.0); the weights are released under the same CC BY-NC 4.0 to match. Human labels by the project's own reviewer. No official-team code or checkpoints are redistributed here.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support