Model Watermarking
Two different things people mean by 'model watermarking'
The term covers two distinct problems that are easy to conflate. Output watermarking marks the content a model generates (text, images, audio) so it can later be detected as AI-generated. Model weight watermarking (or fingerprinting) marks the model itself — its parameters — so ownership can be proven if the weights are stolen or redistributed without authorization. They solve different problems, use different techniques, and have different threat models.
Output watermarking for LLM text: biasing the sampling distribution
The dominant approach for watermarking LLM-generated text (notably the scheme from Kirchenbauer et al., 2023) works at the token-sampling step. For each generated token, the model's vocabulary is pseudo-randomly split into a 'green list' and a 'red list,' with the split seeded deterministically by a hash of the preceding tokens (so the same context always produces the same split, without needing to store anything). The sampling logits for green-list tokens are boosted by a small amount before sampling, subtly biasing generation toward the green list without materially hurting text quality if the bias is small.
import hashlib
import numpy as np
def green_list_mask(prev_token, vocab_size, green_fraction=0.5, boost=2.0):
seed = int(hashlib.sha256(str(prev_token).encode()).hexdigest(), 16) % (2**32)
rng = np.random.default_rng(seed)
green_ids = rng.choice(vocab_size, size=int(vocab_size * green_fraction), replace=False)
mask = np.zeros(vocab_size)
mask[green_ids] = boost
return mask # add this to logits before sampling
def detect_watermark(token_sequence, vocab_size, green_fraction=0.5):
"""Count how many tokens landed in their context's green list; compare to the
~50% expected by chance under a z-test to flag likely watermarked text."""
green_hits = 0
for i in range(1, len(token_sequence)):
mask = green_list_mask(token_sequence[i - 1], vocab_size, green_fraction)
if mask[token_sequence[i]] > 0:
green_hits += 1
expected = (len(token_sequence) - 1) * green_fraction
return green_hits, expected # large excess over expected => likely watermarkedDetection needs no access to the model, only the hashing scheme
Output watermarking vs model weight watermarking
| Property | Output watermarking | Model weight watermarking |
|---|---|---|
| What's marked | Generated content (text/image/audio) | The model's parameters themselves |
| Goal | Detect AI-generated content after the fact | Prove ownership if weights are leaked/stolen |
| Survives paraphrasing/editing? | Often degraded or removed by edits | N/A — concerns redistribution, not editing |
| Typical mechanism | Biased token sampling, statistical detection | Hidden trigger behaviors, parameter perturbation signatures |
Model weight watermarking via backdoor-style triggers
To watermark the weights themselves, one common technique trains the model to respond in a specific, unusual way to a secret trigger input that would almost never occur naturally (a particular nonsensical prompt, or a specific rare input pattern) — producing an idiosyncratic, verifiable output only the model owner knows to check for. If a suspected stolen/redistributed model responds correctly to this trigger, it's strong evidence it derives from the watermarked original. This is architecturally similar to a backdoor attack, repurposed defensively — the same mechanism that's dangerous when planted by an attacker is useful when planted intentionally by the legitimate owner for provenance.
Both kinds of watermarking are fragile against a motivated adversary
What's Next?
Watermarking sits alongside a broader set of model-protection and provenance concerns — model stealing and extraction attacks, membership inference, and backdoor attacks — all of which revolve around the same underlying question of what a model's owner can prove, detect, or defend against once a model leaves their direct control.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.