
Implementación de referencia de un esquema de marca de agua LLM multibit que utiliza dispersión de carga codificada, reponderación imparcial y decodificación ECC de decisión suave para incrustar y detectar mensajes en texto generado.
Implementación de referencia de Robust and Scalable Multi-bit LLM Watermarking via Coded Payload Spreading.
WeaveMark incrusta un mensaje de k bits en texto generado por LLM manteniendo la distribución de tokens imparcial. Combina la dispersión de carga codificada (κ bits de palabra de código por token sobre ℓ capas, con una asignación capa→bit barajada por contexto), decodificación ECC de decisión suave a partir de los márgenes de voto por bit, y reponderación multicapa imparcial (cada dirección XORed con un lanzamiento de moneda de contexto). Capas dedicadas de cero bits, particionadas del contexto en lugar del mensaje, proporcionan detección de presencia independiente del mensaje.
weavemark/ biblioteca
watermark.py procesador de logits de WeaveMark (incrustación)
extraction.py extract_bits, detect_zerobit
prf.py PRF con clave (compartido por embed/extract)
data.py carga de datasets, preparación de prompts, registros JSONL
device.py verificación de CUDA
ecc/ decodificadores Golay, Reed-Muller, de decisión suave
generate.py generar texto con marca de agua / texto plano
detect.py extracción + z-score de cero bits + PPL
evaluate_downstream.py calidad de resumen / traducción
perplexity.py puntuador PPL
attacks/ sustitución, DIPPER
run_*.py pipelines (generate+detect, ataques, downstream)
data/OpenGen.jsonl prompts de OpenGen (--dataset opengen); ver nota abajo
data/OpenGen.jsonl (el benchmark público estándar de OpenGen) se omite de este
archivo por tamaño; colócalo bajo data/ para usar --dataset opengen. Los
datasets c4 y openwebtext se transmiten desde el Hub y no necesitan archivo local.
Ejecuta todos los scripts desde la raíz del repositorio para que import weavemark se resuelva.
Requiere Python 3.10–3.12 y una GPU CUDA (los modelos se cargan en 4 bits vía bitsandbytes; no hay ruta para CPU). LLaMA-3-8B en 4 bits cabe en ~16 GB.
cd WeaveMark
python -m venv .venv && source .venv/bin/activate # o: conda create -n weavemark python=3.11
Instala una compilación CUDA de PyTorch antes de requirements.txt — un simple
pip install torch da un wheel solo-CPU en Windows, y el wheel debe tener
kernels para tu GPU (RTX serie 50 / sm_120 necesita torch ≥ 2.7 en CUDA ≥ 12.8;
los wheels cu121 más antiguos se detienen en sm_90 y fallan en tiempo de ejecución).
Elige el comando correspondiente de https://pytorch.org.
pip install torch --index-url https://download.pytorch.org/whl/cu128 # ejemplo: Blackwell
pip install -r requirements.txt
Comprueba que la GPU realmente ejecuta kernels (is_available() por sí solo no basta — es
True incluso cuando el wheel carece de kernels para la arquitectura):
python -c "import torch; t=torch.zeros(8).cuda().normal_(); print(torch.__version__, torch.cuda.get_device_capability(), (t*t).sum().item())"
LLaMA-3 y C4 están restringidos: huggingface-cli login y acepta sus términos.
OpenGen se incluye con el repositorio. Selecciona una GPU con CUDA_VISIBLE_DEVICES=<i>.
python generate.py --method weavemark --ecc_method rm --random_message \
--dataset c4 --num_test 100 --max_new_tokens 200 \
--bpt 10 --num_layers 10 --window_size 2 --prob_delta 1.0 \
--num_zerobit_layers 0 --do_sample
python detect.py --data_dir output_dump/<run_folder> --detect --stride 25
detect.py escribe detailed_eval_*.csv y summary_eval_*.csv; las columnas clave
son success_rate y, con --zerobit_threshold, z_success_rate.
run_main.py encadena generate + detect (edita las constantes en su parte superior).
Datasets: c4 / openwebtext se transmiten desde el Hub y se truncan a
--prompt_len palabras; opengen lee data/OpenGen.jsonl y trunca cada
prefijo a --prompt_len tokens.
ECC msg→codeword: golay 12→24, rm 16→32, dual_golay 24→48, dual_rm
32→64. none incrusta --message sin codificar.
Configuraciones del artículo: el rastreo multibit usa --bpt 10 --num_layers 10 --window_size 2 --prob_delta 1.0 --num_zerobit_layers 0, ECC según la longitud del mensaje; añade
--num_zerobit_layers 2 --enable_zerobit para la configuración combinada; cero bits puro
es --bpt 0 --num_layers 0 --num_zerobit_layers 10 --enable_zerobit.
python attacks/substitution.py --data_dir output_dump/<run> --ratios 0.1 0.2 0.3
python attacks/dipper_attack.py --data_dir output_dump/<run> --lex_diversity 20 --order_diversity 20
python detect.py --data_dir output_dump/<run> --text_file attacked_text_10.jsonl --detect --stride 999
run_substitution.py / run_dipper.py encadenan ataque + detección de texto completo
(--stride 999 evalúa cada recorte completo). run_downstream.py ejecuta
resumen (CNN/DailyMail) y traducción (WMT16 en→ro) con BERTScore +
ROUGE/BLEU.
window_size = 2: el diseño imparcial prohíbe reutilizar un contexto, por lo que una ventana
más grande reduce la pérdida por omisión (ver apéndice).no_watermark reutilizan las mismas claves/parámetros para medir la tasa
de falsos positivos.@inproceedings{weavemark,
title = {Robust and Scalable Multi-bit LLM Watermarking via Coded Payload Spreading},
author = {Anonymous},
year = {2026},
note = {Under review}
}
attacks/dipper.py está adaptado de
martiansideofthemoon/ai-detection-paraphrases
(su licencia se aplica a ese archivo). Publicado bajo MIT (ver LICENSE); establece el
titular de los derechos de autor antes de publicar.
| Flag | Símbolo | Significado | Típico |
|---|
--bpt | κ | bits por token | 10 |
--num_layers | ℓ | capas multibit | 10 |
--window_size | h | ventana de contexto | 2 |
--prob_delta | δ | fuerza de reponderación | 1.0 |
--ecc_method | — | none/golay/dual_golay/rm/dual_rm | rm |
--num_zerobit_layers | ℓ_z | capas de cero bits | 0 o 2 |
--top_k | K | top-k de muestreo | 50 |