
Эталонная реализация многовитной схемы водяных знаков для LLM, использующей кодированное расширение полезной нагрузки, несмещённое перевзвешивание и мягкое декодирование с помехоустойчивым кодированием (ECC) для встраивания и обнаружения сообщений в сгенерированном тексте.
Эталонная реализация метода Robust and Scalable Multi-bit LLM Watermarking via Coded Payload Spreading (надёжное и масштабируемое многовитное маркирование LLM через кодовое распределение полезной нагрузки).
WeaveMark встраивает k-битное сообщение в текст, сгенерированный LLM, сохраняя распределение токенов несмещённым. Он сочетает кодовое распределение полезной нагрузки (κ бит кодового слова на токен по ℓ слоям, с перемешиваемым по контексту назначением слой→бит), мягкое декодирование помехоустойчивого кода (ECC) по полям голосования каждого бита и несмещённую многослойную перевзвешивающую схему (каждое направление XORится с подбрасыванием монеты, зависящим от контекста). Выделенные нуль-битные слои, отделённые от контекста, а не от сообщения, обеспечивают независимое от сообщения обнаружение присутствия.
weavemark/ библиотека
watermark.py процессор логитов WeaveMark (встраивание)
extraction.py extract_bits, detect_zerobit
prf.py ключевой PRF (общий для встраивания/извлечения)
data.py загрузка наборов данных, подготовка подсказок, записи JSONL
device.py проверка CUDA
ecc/ декодеры Голея, Рида–Маллера, мягкого решения
generate.py генерация маркированного / обычного текста
detect.py извлечение + z-оценка нуль-бита + PPL
evaluate_downstream.py качество суммаризации / перевода
perplexity.py оценка PPL
attacks/ подстановка, DIPPER
run_*.py конвейеры (generate+detect, атаки, downstream)
data/OpenGen.jsonl подсказки OpenGen (--dataset opengen); см. примечание ниже
data/OpenGen.jsonl (стандартный публичный бенчмарк OpenGen) исключён из этого
архива из-за размера; поместите его в каталог data/, чтобы использовать
--dataset opengen. Наборы данных c4 и openwebtext передаются потоком с Hub
и не требуют локального файла.
Запускайте все скрипты из корня репозитория, чтобы разрешался import weavemark.
Требуется Python 3.10–3.12 и GPU с CUDA (модели загружаются в 4-битном формате через bitsandbytes; пути для CPU нет). 4-битная LLaMA-3-8B помещается в ~16 ГБ.
cd WeaveMark
python -m venv .venv && source .venv/bin/activate # или: conda create -n weavemark python=3.11
Установите сборку PyTorch для CUDA до requirements.txt — обычная
pip install torch даёт CPU-only колесо на Windows, и колесо должно содержать
ядра для вашего GPU (RTX 50-й серии / sm_120 требует torch ≥ 2.7 на CUDA ≥ 12.8;
более старые колеса cu121 останавливаются на sm_90 и падают во время выполнения).
Выберите подходящую команду на https://pytorch.org.
pip install torch --index-url https://download.pytorch.org/whl/cu128 # пример: Blackwell
pip install -r requirements.txt
Проверьте, что GPU действительно выполняет ядра (одного is_available()
недостаточно — он возвращает True, даже если в колесе нет ядер для данной
архитектуры):
python -c "import torch; t=torch.zeros(8).cuda().normal_(); print(torch.__version__, torch.cuda.get_device_capability(), (t*t).sum().item())"
LLaMA-3 и C4 ограничены доступом: выполните huggingface-cli login и примите их
условия. OpenGen поставляется вместе с репозиторием. Выберите GPU с помощью
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 записывает detailed_eval_*.csv и summary_eval_*.csv; ключевые
столбцы — success_rate и, с --zerobit_threshold, z_success_rate.
run_main.py объединяет generate + detect (отредактируйте константы в его начале).
Наборы данных: c4 / openwebtext передаются потоком с Hub и усекаются до
--prompt_len слов; opengen читает data/OpenGen.jsonl и усекает каждый
префикс до --prompt_len токенов.
ECC сообщение→кодовое слово: golay 12→24, rm 16→32, dual_golay 24→48,
dual_rm 32→64. none встраивает --message без кодирования.
Настройки из статьи: многовитное отслеживание использует --bpt 10 --num_layers 10 --window_size 2 --prob_delta 1.0 --num_zerobit_layers 0, ECC по длине
сообщения; добавьте --num_zerobit_layers 2 --enable_zerobit для комбинированного
режима; чисто нуль-битный режим — --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 объединяют атаку + обнаружение по всему
тексту (--stride 999 оценивает каждый фрагмент целиком). run_downstream.py
запускает суммаризацию (CNN/DailyMail) и перевод (WMT16 en→ro) с BERTScore +
ROUGE/BLEU.
window_size = 2: несмещённая схема запрещает повторное использование
контекста, поэтому большее окно снижает потери от пропусков (см. приложение).no_watermark используют те же ключи/параметры для измерения частоты
ложных срабатываний.@inproceedings{weavemark,
title = {Robust and Scalable Multi-bit LLM Watermarking via Coded Payload Spreading},
author = {Anonymous},
year = {2026},
note = {Under review}
}
attacks/dipper.py адаптирован из
martiansideofthemoon/ai-detection-paraphrases
(его лицензия применяется к этому файлу). Выпущено под MIT (см. LICENSE);
укажите правообладателя перед публикацией.
| Флаг | Символ | Значение | Типичное |
|---|
--bpt | κ | бит на токен | 10 |
--num_layers | ℓ | многовитные слои | 10 |
--window_size | h | окно контекста | 2 |
--prob_delta | δ | сила перевзвешивания | 1.0 |
--ecc_method | — | none/golay/dual_golay/rm/dual_rm | rm |
--num_zerobit_layers | ℓ_z | нуль-битные слои | 0 или 2 |
--top_k | K | top-k сэмплирование | 50 |