DiPmark (DIP) Watermarking
Permutation-based probability redistribution that uses cumulative-probability quantile splitting instead of a flat logit bias.
Example code: 📓 View On GitHub
Author: Wu et al.
Theory
Unlike green-list methods (KGW, Unigram) that add a flat \(\delta\) to logits, DiPmark generates a random vocabulary permutation \(\pi\) for each context and redistributes probability mass using cumulative-probability quantile splitting.
For a given context, the algorithm:
Generates a deterministic permutation \(\pi\) of the vocabulary by hashing the context with a secret key
Sorts token probabilities according to \(\pi\)
Computes cumulative probabilities in the permuted order
Splits the distribution at quantile boundaries \(\alpha\) and \(1-\alpha\), boosting tokens in the upper portion
The parameter \(\alpha \in [0, 0.5]\) controls the watermarking strength. Unlike KGW’s flat delta, the probability boost adapts to the token’s position in the cumulative distribution, making the watermark more stealthy.
Detection checks each token’s quantile position in the context-seeded permutation. Tokens landing in the boosted portion (quantile \(\geq \gamma\)) are counted as “green”, and a z-score is computed against the binomial null.
Note
DiPmark tracks context history to avoid over-watermarking repeated n-grams. Contexts seen before are skipped during both generation and detection.
Paper reference
Wu, Y., Hu, Z., Zhang, H., & Huang, H. (2023). DiPmark: A Stealthy, Efficient and Resilient Watermark for Large Language Models. arXiv preprint arXiv:2310.07710. https://arxiv.org/abs/2310.07710
Example code
import os
os.environ["VLLM_USE_V1"] = "1"
os.environ["VLLM_ENABLE_V1_MULTIPROCESSING"] = "0"
from vllm import LLM, SamplingParams
from vllm_watermark.core import (
DetectionAlgorithm,
WatermarkedLLMs,
WatermarkingAlgorithm,
)
from vllm_watermark.watermark_detectors import WatermarkDetectors
llm = LLM(model="meta-llama/Llama-3.2-1B", enforce_eager=True, max_model_len=1024)
wm_llm = WatermarkedLLMs.create(
model=llm,
algo=WatermarkingAlgorithm.DIP,
seed=42,
ngram=2,
alpha=0.45,
gamma=0.5,
hash_key=15485863,
)
detector = WatermarkDetectors.create(
algo=DetectionAlgorithm.DIP,
model=llm,
ngram=2,
seed=42,
gamma=0.5,
hash_key=15485863,
threshold=0.05,
)
prompts = ["Write about machine learning applications"]
sampling_params = SamplingParams(temperature=1.0, top_p=0.95, max_tokens=128)
outputs = wm_llm.generate(prompts, sampling_params)
for output in outputs:
generated_text = output.outputs[0].text
result = detector.detect(generated_text)
print(f"Watermarked: {result['is_watermarked']}, P-value: {result['pvalue']:.6f}")