
다중 비트 LLM 워터마킹 기법의 참조 구현으로, 코딩된 페이로드 확산, 편향 없는 재가중치 부여, 소프트 결정 ECC 디코딩을 사용하여 생성된 텍스트에 메시지를 삽입하고 탐지합니다.
코딩된 페이로드 확산을 통한 견고하고 확장 가능한 멀티비트 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/ Golay, Reed-Muller, 소프트 결정 디코더
generate.py 워터마크/일반 텍스트 생성
detect.py 추출 + 제로비트 z-점수 + PPL
evaluate_downstream.py 요약 / 번역 품질
perplexity.py PPL 스코어러
attacks/ 대체(substitution), DIPPER
run_*.py 파이프라인(생성+탐지, 공격, 다운스트림)
data/OpenGen.jsonl OpenGen 프롬프트(--dataset opengen); 아래 참고
data/OpenGen.jsonl(표준 공개 OpenGen 벤치마크)은 크기 때문에 이 아카이브에서 제외되었습니다. --dataset opengen을 사용하려면 data/ 아래에 배치하세요. c4 및 openwebtext 데이터셋은 Hub에서 스트리밍되며 로컬 파일이 필요 없습니다.
import weavemark가 해석되도록 모든 스크립트를 저장소 루트에서 실행하세요.
Python 3.10–3.12 및 CUDA GPU가 필요합니다(모델은 bitsandbytes를 통해 4비트로 로드되며 CPU 경로는 없습니다). 4비트 LLaMA-3-8B는 약 16GB에 맞습니다.
cd WeaveMark
python -m venv .venv && source .venv/bin/activate # 또는: conda create -n weavemark python=3.11
requirements.txt 이전에 CUDA 빌드의 PyTorch를 설치하세요 — 일반 pip install torch는 Windows에서 CPU 전용 휠을 제공하며, 휠에는 GPU용 커널이 있어야 합니다(RTX 50 시리즈 / sm_120은 CUDA ≥ 12.8에서 torch ≥ 2.7 필요; 이전 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은 저장소와 함께 제공됩니다. CUDA_VISIBLE_DEVICES=<i>로 GPU를 선택하세요.
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는 생성 + 탐지를 연결합니다(상단의 상수를 편집하세요).
데이터셋: 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는 BERTScore + ROUGE/BLEU로 요약(CNN/DailyMail) 및 번역(WMT16 en→ro)을 실행합니다.
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 |