浮水印移除技術
進階4 分鐘閱讀更新於 2026-03-13
移除 AI 浮水印的技術:換句話攻擊、token 替換、embedding 空間擾動,及其對模型來源與可究責性的意涵。
AI 浮水印的設計目的是標記文字為 AI 產生,以啟動歸屬與來源追蹤。然而當前浮水印方案易受移除攻擊——可剝除統計訊號同時保留文字品質。這對模型可究責性、內容來源與智慧財產保護具重大意涵。
AI 浮水印的運作方式
Green-Red List 做法(Kirchenbauer 等)
最廣為研究的做法:於每步生成時,使用秘密金鑰將詞彙分割為「green」與「red」清單:
import hashlib
import torch
def watermarked_sampling(logits, prev_token, watermark_key, bias=2.0):
"""取樣時對 'green list' token 加偏誤。
green list 由前一 token 與浮水印金鑰之 hash 決定。"""
# 依 前一 token + 金鑰 之確定性 green/red 分割
seed = int(hashlib.sha256(
f"{watermark_key}:{prev_token}".encode()
).hexdigest()[:8], 16)
rng = torch.Generator().manual_seed(seed)
vocab_size = logits.shape[-1]
perm = torch.randperm(vocab_size, generator=rng)
green_tokens = perm[:vocab_size // 2]
# 對 logits 向 green tokens 加偏誤
watermarked_logits = logits.clone()
watermarked_logits[green_tokens] += bias
return watermarked_logits
def detect_watermark(text, tokenizer, watermark_key, threshold=0.5):
"""藉由測量 green token 比例偵測浮水印。"""
tokens = tokenizer.encode(text)
green_count = 0
for i in range(1, len(tokens)):
seed = int(hashlib.sha256(
f"{watermark_key}:{tokens[i-1]}".encode()
).hexdigest()[:8], 16)
rng = torch.Generator().manual_seed(seed)
perm = torch.randperm(tokenizer.vocab_size, generator=rng)
green_tokens = set(perm[:tokenizer.vocab_size // 2].tolist())
if tokens[i] in green_tokens:
green_count += 1
green_fraction = green_count / (len(tokens) - 1)
# 無浮水印文字:~50% green tokens
# 有浮水印文字:顯著 > 50% green tokens
z_score = (green_fraction - 0.5) / (0.5 / (len(tokens) - 1) ** 0.5)
return z_score > threshold, z_score, green_fraction浮水印偵測性質
| 性質 | 值 | 意涵 |
|---|---|---|
| 期望 green 比例(無浮水印) | ~50% | 統計基準 |
| 期望 green 比例(含浮水印) | 65–85% | 取決於偏誤強度 |
| 最小可偵測文字長度 | ~25 tokens | 短文不可靠 |
| 偽陽性率(z > 4) | ~0.003% | 對長文非常低 |
| 偽陰性率 | 取決於偏誤強度 | 偏誤愈高,FN 率愈低 |
移除技術 1:換句話攻擊
最有效且最易取得的移除技術:以不同(未加浮水印)模型將含浮水印文字換句話。
def paraphrase_removal(watermarked_text, paraphrase_model, tokenizer):
"""以未加浮水印之模型換句話以移除浮水印。"""
prompt = f"""Rewrite the following text to convey the same meaning
using different words and sentence structures. Preserve all factual
content and technical accuracy.
Original text:
{watermarked_text}
Rewritten text:"""
# 換句話模型產生不含浮水印偏誤之新 token
paraphrased = paraphrase_model.generate(
tokenizer(prompt, return_tensors="pt").input_ids,
max_new_tokens=len(watermarked_text.split()) * 2,
temperature=0.7,
)
return tokenizer.decode(paraphrased[0], skip_special_tokens=True)換句話的有效性
| 換句話方法 | 浮水印移除率 | 語意保留 | 品質 |
|---|---|---|---|
| LLM 換句話(GPT-4 級) | 85–95% | 90–95% | 高 |
| LLM 換句話(7B 模型) | 70–85% | 80–90% | 中 |
| 回譯(EN→FR→EN) | 60–80% | 75–85% | 中 |
| 同義詞替換 | 40–60% | 85–95% | 高 |
| 隨機詞刪除/插入 | 30–50% | 60–80% | 低 |
移除技術 2:Token 替換
有針對性地替換 token,使 green token 比例回歸 50% 基準:
def token_substitution_removal(text, tokenizer, watermark_key, model):
"""以語意相當之 red token 選擇性替換 green token
以移除浮水印。"""
tokens = tokenizer.encode(text)
modified_tokens = list(tokens)
for i in range(1, len(tokens)):
# 判斷當前 token 是否為 green
green_set = get_green_tokens(tokens[i-1], watermark_key, tokenizer.vocab_size)
if tokens[i] in green_set:
# 尋找語意相近之 red token
# 取得該位置上模型之機率分布
context = torch.tensor([modified_tokens[:i]])
logits = model(context).logits[0, -1]
# 尋找最高機率之 red token
red_set = set(range(tokenizer.vocab_size)) - green_set
red_probs = [(t, logits[t].item()) for t in red_set]
red_probs.sort(key=lambda x: x[1], reverse=True)
best_red_token = red_probs[0][0]
# 僅在 red token 機率合理時才替換
if logits[best_red_token] > logits[tokens[i]] - 3.0:
modified_tokens[i] = best_red_token
return tokenizer.decode(modified_tokens)移除技術 3:Embedding 空間擾動
對於在 embedding 空間運作的浮水印,擾動可擾亂浮水印訊號:
def embedding_perturbation_removal(text, model, tokenizer, noise_scale=0.1):
"""藉由對 token embedding 加雜訊並重新解碼,
移除 embedding 層級之浮水印。"""
tokens = tokenizer(text, return_tensors="pt")
embeddings = model.get_input_embeddings()(tokens.input_ids)
# 加入校準雜訊以擾亂浮水印訊號
noise = torch.randn_like(embeddings) * noise_scale
perturbed_embeddings = embeddings + noise
# 自擾動後之 embedding 重新解碼
with torch.no_grad():
outputs = model(inputs_embeds=perturbed_embeddings)
new_tokens = outputs.logits.argmax(dim=-1)
return tokenizer.decode(new_tokens[0], skip_special_tokens=True)對模型來源的意涵
浮水印軍備競賽
| 世代 | 浮水印做法 | 移除攻擊 | 結果 |
|---|---|---|---|
| 第 1 代 | Green-red token 偏誤 | 換句話 | 浮水印被移除 |
| 第 2 代 | 語意浮水印 | 風格轉換 | 部分有效 |
| 第 3 代 | 多層次浮水印 | 集成換句話 | 貓鼠遊戲持續 |
當前限制
其他來源追蹤做法
| 做法 | 機制 | 對移除之穩健度 |
|---|---|---|
| 文字浮水印 | Token 選擇中的統計偏誤 | 低 —— 換句話即可移除 |
| 模型指紋 | 模型權重中獨特的行為簽名 | 中 —— 可撐過文字層級攻擊 |
| 文體計量分析 | 統計寫作風格特徵 | 中 —— 依文字長度而異 |
| 以檢索為本之偵測 | 與已知輸出資料庫比對 | 高 —— 需完整資料庫 |
| 密碼學簽章 | 生成時對輸出簽章 | 高 —— 但需信任簽章金鑰 |
倫理考量
相關主題
Knowledge Check
為何以 LLM 為本之換句話是最有效的浮水印移除技術?