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/ Golay、Reed-Muller、軟判定復号器
generate.py 透かし付き/プレーンテキストの生成
detect.py 抽出 + ゼロビットzスコア + PPL
evaluate_downstream.py 要約 / 翻訳品質
perplexity.py PPLスコアラー
attacks/ 置換、DIPPER
run_*.py パイプライン(generate+detect、攻撃、ダウンストリーム)
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は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は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 |