
Implementação de referência de um esquema de marca d'água LLM multibit usando espalhamento de payload codificado, reweighting imparcial e decodificação ECC de decisão suave para incorporar e detectar mensagens em texto gerado.
Implementação de referência de Robust and Scalable Multi-bit LLM Watermarking via Coded Payload Spreading.
O WeaveMark incorpora uma mensagem de k bits em texto gerado por LLM, mantendo a distribuição de tokens imparcial. Ele combina espalhamento de payload codificado (κ bits de palavra-código por token em ℓ camadas, com uma atribuição camada→bit embaralhada por contexto), decodificação ECC de decisão suave a partir de margens de voto por bit e re-ponderação multicamada imparcial (cada direção submetida a XOR com um lançamento de moeda contextual). Camadas dedicadas de zero bits, particionadas do contexto em vez da mensagem, fornecem detecção de presença independente da mensagem.
weavemark/ biblioteca
watermark.py processador de logits WeaveMark (incorporação)
extraction.py extract_bits, detect_zerobit
prf.py PRF com chave (compartilhado por incorporar/extrair)
data.py carregamento de conjuntos de dados, preparação de prompts, registros JSONL
device.py verificação CUDA
ecc/ Golay, Reed-Muller, decodificadores de decisão suave
generate.py gera texto com marca d'água / texto simples
detect.py extração + z-score de zero bits + PPL
evaluate_downstream.py qualidade de sumarização / tradução
perplexity.py avaliador de PPL
attacks/ substituição, DIPPER
run_*.py pipelines (gerar+detectar, ataques, downstream)
data/OpenGen.jsonl prompts OpenGen (--dataset opengen); veja nota abaixo
data/OpenGen.jsonl (o benchmark público padrão OpenGen) é omitido deste
arquivo por tamanho; coloque-o em data/ para usar --dataset opengen. Os
conjuntos de dados c4 e openwebtext são transmitidos do Hub e não precisam de arquivo local.
Execute todos os scripts a partir da raiz do repositório para que import weavemark seja resolvido.
Requer Python 3.10–3.12 e uma GPU CUDA (os modelos carregam em 4 bits via bitsandbytes; não há caminho para CPU). LLaMA-3-8B em 4 bits cabe em ~16 GB.
cd WeaveMark
python -m venv .venv && source .venv/bin/activate # ou: conda create -n weavemark python=3.11
Instale uma compilação CUDA do PyTorch antes de requirements.txt — um simples
pip install torch fornece um wheel somente CPU no Windows, e o wheel deve ter
kernels para sua GPU (RTX série 50 / sm_120 requer torch ≥ 2.7 em CUDA ≥ 12.8;
wheels cu121 mais antigos param em sm_90 e falham em tempo de execução). Escolha o comando
correspondente em https://pytorch.org.
pip install torch --index-url https://download.pytorch.org/whl/cu128 # exemplo: Blackwell
pip install -r requirements.txt
Verifique se a GPU realmente executa kernels (is_available() sozinho não é suficiente — ele retorna
True mesmo quando o wheel não possui kernels para a arquitetura):
python -c "import torch; t=torch.zeros(8).cuda().normal_(); print(torch.__version__, torch.cuda.get_device_capability(), (t*t).sum().item())"
LLaMA-3 e C4 são restritos: huggingface-cli login e aceite seus termos.
O OpenGen acompanha o repositório. Selecione uma GPU com 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 grava detailed_eval_*.csv e summary_eval_*.csv; as principais colunas
são success_rate e, com --zerobit_threshold, z_success_rate.
run_main.py encadeia gerar + detectar (edite as constantes em seu topo).
Conjuntos de dados: c4 / openwebtext são transmitidos do Hub e truncados para
--prompt_len palavras; opengen lê data/OpenGen.jsonl e trunca cada
prefixo para --prompt_len tokens.
ECC msg→palavra-código: golay 12→24, rm 16→32, dual_golay 24→48, dual_rm
32→64. none incorpora --message sem codificação.
Configurações do artigo: rastreamento multibit usa --bpt 10 --num_layers 10 --window_size 2 --prob_delta 1.0 --num_zerobit_layers 0, ECC por comprimento da mensagem; adicione
--num_zerobit_layers 2 --enable_zerobit para a configuração combinada; zero bits puro
é --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 encadeiam ataque + detecção de texto inteiro
(--stride 999 avalia cada trecho inteiro). run_downstream.py executa
sumarização (CNN/DailyMail) e tradução (WMT16 en→ro) com BERTScore +
ROUGE/BLEU.
window_size = 2: o design imparcial proíbe reutilizar um contexto, portanto uma janela
maior reduz a perda por salto (veja o apêndice).no_watermark reutilizam as mesmas chaves/parâmetros para medir a taxa 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 é adaptado de
martiansideofthemoon/ai-detection-paraphrases
(sua licença se aplica a esse arquivo). Lançado sob MIT (veja LICENSE); defina o
detentor dos direitos autorais antes de publicar.
| Sinalizador | Símbolo | Significado | Típico |
|---|
--bpt | κ | bits por token | 10 |
--num_layers | ℓ | camadas multibit | 10 |
--window_size | h | janela de contexto | 2 |
--prob_delta | δ | força de re-ponderação | 1.0 |
--ecc_method | — | none/golay/dual_golay/rm/dual_rm | rm |
--num_zerobit_layers | ℓ_z | camadas de zero bits | 0 ou 2 |
--top_k | K | top-k de amostragem | 50 |