Overtone maskers
128-bit semantic hash heads ("maskers") for the Overtone image-watermark verifier. Each masker maps frozen image-encoder features to a 128-bit code; images generated from the same prompt should land at small Hamming distance, images from different prompts far apart.
All runs use the two-stage recipe (stage 1: supervised contrastive on features; stage 2:
hash contrast) from experiments/train_masker2s.py.
Recommended: masker2s_semcon/concat_paper_best/.
Layout
Each folder holds model.pt (state dict of the checkpoint selected by validation FPR at 99%
genuine TPR) and history.json (training args and per-step validation metrics).
| folder | input features | train data | P×M | d′ | FPR@95 | FPR@99 |
|---|---|---|---|---|---|---|
masker2s_semcon/concat_paper_best (best) |
DINOv2-g + CLIP-B/32 | SemCon 96k | 256×4 | 6.28 | 0.0039 | 0.0132 |
masker2s_semcon/concat_paper_M8 |
DINOv2-g + CLIP-B/32 | SemCon 96k | 128×8 | 6.34 | 0.0047 | 0.0148 |
masker2s_semcon/dino_paper |
DINOv2-g | SemCon 96k | 256×4 | 5.98 | 0.0053 | 0.0185 |
masker2s_enc/dino_clip_b32_clip_h14_siglip |
DINOv2-g + CLIP-B/32 + CLIP-H/14 + SigLIP | COCO 26k | 256×4 | 6.20 | 0.0047 | 0.0140 |
masker2s_enc/dino_clip_h14_siglip |
DINOv2-g + CLIP-H/14 + SigLIP | COCO 26k | 256×4 | 6.04 | 0.0048 | 0.0150 |
masker2s_enc/dino_siglip |
DINOv2-g + SigLIP | COCO 26k | 256×4 | 5.87 | 0.0052 | 0.0167 |
masker2s_enc/dino_clip_h14 |
DINOv2-g + CLIP-H/14 | COCO 26k | 256×4 | 5.82 | 0.0053 | 0.0169 |
masker2s_enc/dino_clip_b32 |
DINOv2-g + CLIP-B/32 | COCO 26k | 256×4 | 5.70 | 0.0069 | 0.0207 |
masker2s/concat_paper |
DINOv2-g + CLIP-B/32 | COCO 26k | 256×4 | 5.71 | 0.0059 | 0.0217 |
masker2s/concat_paper_M8 |
DINOv2-g + CLIP-B/32 | COCO 26k | 128×8 | 6.02 | 0.0072 | 0.0223 |
masker2s/concat_bn |
DINOv2-g + CLIP-B/32 | COCO 26k | 256×4 | 5.27 | 0.0068 | 0.0259 |
masker2s/dino_paper |
DINOv2-g | COCO 26k | 256×4 | 5.49 | 0.0072 | 0.0286 |
Metrics are those of the selected checkpoint on its own validation prompts, as recorded in
history.json (Hamming distance; FPR = fraction of different-prompt pairs within the 95th /
99th percentile of same-prompt distances). The SemCon and COCO-26k runs use different
validation sets, so compare within a group. concat_bn uses a BatchNorm-before-tanh stage 2
instead of the paper's scale schedule; all others use the paper schedule.
- SemCon 96k: 96,041 prompts (COCO train + SDP, deduplicated by BGE embedding + k-means), 16 generations + 16 distorted views each.
- COCO 26k: 26,584 COCO captions, 6 generations + 2 distorted views each.
Input
The input is the L2-normalised CLS embedding of each encoder, concatenated and divided by
sqrt(number of encoders). The model standardises it internally (mu/sd buffers). The code is
the sign of the 128-d output.
Loading
The state dict loads into Masker from experiments/train_masker128.py:
import json, torch
from huggingface_hub import hf_hub_download
from train_masker128 import Masker
folder = "masker2s_semcon/concat_paper_best"
args = json.load(open(hf_hub_download("ehanw/overtone-masker", f"{folder}/history.json")))["args"]
sd = torch.load(hf_hub_download("ehanw/overtone-masker", f"{folder}/model.pt"), map_location="cpu")
model = Masker(sd["mu"].numel(), args["hash_dim"], args["feat_dim"], bn=args["stage2"] == "bn")
model.load_state_dict(sd)
model.eval()
bits = model(x) > 0 # x: (N, in_dim) features as described above