AI Watermarking
Đánh dấu nội dung AI
Một sinh viên nộp bài luận dài 500 chữ. Làm sao giáo viên biết bài do AI viết mà KHÔNG cần đọc nội dung, không cần so sánh với corpus, chỉ dùng phân tích toán học?
Dưới đây là ba đoạn văn: hai đoạn do LLM có watermark sinh ra, một đoạn do người thật viết. Hover vào từng từ để xem trạng thái green / red, log-prob nền và bias delta được cộng. Nhấn nút để hiện/ẩn lớp watermark. Điều chỉnh gamma, delta, và ngưỡng z để xem độ nhạy thay đổi.
Hình minh họa
Văn bản do một LLM có watermark (gamma=0.5, delta=2.0) tạo ra. Tỷ lệ green tokens sẽ cao bất thường.
Gamma nhỏ = green hiếm, khó phát hiện. Gamma = 0.5 là mặc định.
Delta cao = watermark mạnh nhưng giảm chất lượng văn bản.
z > 4 ⇔ p < 3×10⁻⁵. Cao hơn = an toàn hơn với false positives.
Một sinh viên dùng ChatGPT viết bài 500 từ, rồi paraphrase (viết lại bằng từ đồng nghĩa) 30% số câu. Watermark còn phát hiện được không?
Văn bản chỉ có 20 tokens, tỷ lệ green là 15/20 = 75%. Có thể kết luận chắc chắn là AI viết không?
Giải thích
AI Watermarking là kỹ thuật nhúng dấu hiệu ẩn vào nội dung AI, cho phép phát hiện nguồn gốc mà không ảnh hưởng đáng kể đến chất lượng. Đây là tuyến phòng thủ quan trọng bổ trợ cho phát hiện deepfake, và đối phó với tấn công đối kháng nhằm xoá dấu nguồn gốc. Trong bối cảnh EU AI Act và quản trị AI tại Việt Nam, watermark đang trở thành yêu cầu bắt buộc đối với nội dung tổng hợp.
1. Chia từ vựng: Dùng hash(token[i-1]) để chia toàn bộ từ vựng thành green list (tỷ lệ gamma, thường 0.5) và red list. Chia ngẫu nhiên nhưng deterministic — cùng prev token luôn cho cùng chia.
2. Thiên vị khi sinh: Cộng bias delta (thường 2.0) vào logits của green tokens trước khi softmax. LLM sẽ ưu tiên chọn từ green, nhưng không ép buộc — nếu red token quá mạnh về ngữ nghĩa thì vẫn có thể được chọn.
3. Phát hiện:Với văn bản cần kiểm tra, tái tạo green list từ hash(token trước) rồi đếm số green. Tính z-score (one-proportion z-test). Nếu z > threshold (4.0), tuyên bố có watermark.
4. False positive rate:Với z > 4, xác suất false positive là p < 3×10⁻⁵ — tức là chưa đến 1 trên 30.000 văn bản con người viết bị gắn nhầm nhãn "AI".
Công thức z-score phát hiện watermark:
Dưới giả thuyết H0 (văn bản không có watermark), số green tokens tuân theo phân phối nhị thức Binomial(T, gamma). Với T đủ lớn, z-score xấp xỉ phân phối chuẩn N(0, 1). Nếu , p-value < 3×10⁻⁵ → bác bỏ H0.
Tokenization: BPE của GPT/LLaMA chia tiếng Việt thành 2-3 tokens/từ (nhiều hơn tiếng Anh ~1.5-2x) → effective vocabulary nhỏ hơn, green/red partition kém đa dạng, watermark yếu hơn tương đối.
Paraphrase: Tiếng Việt có tính linh hoạt cao trong diễn đạt (đồng nghĩa dày đặc, từ Hán Việt vs thuần Việt) → dễ paraphrase xoá watermark mà không mất nghĩa.
Ảnh AI: Watermark cho ảnh (Stable Diffusion, DALL-E, Midjourney) nhúng vào miền tần số không gian DFT/DCT — bền hơn với chỉnh sửa, crop, resize. Phương pháp khác hẳn văn bản.
Tin giả: Mạng xã hội Việt Nam bùng nổ deepfake chính trị; watermark chỉ phát hiện nội dung do mô hình có watermark sinh ra — mô hình open-source không watermark vẫn là lỗ hổng.
"""
Phát hiện watermark Kirchenbauer bằng z-score.
Yêu cầu: tokenizer của mô hình gốc, gamma đã biết.
"""
import hashlib
import numpy as np
from typing import List, Dict
def _green_list_for_context(
prev_token_id: int,
vocab_size: int,
gamma: float = 0.5,
secret_key: bytes = b"vi-edu-demo-key",
) -> set[int]:
"""Sinh green list từ hash(khoá + prev_token).
Trả về tập các token id thuộc green list.
"""
payload = secret_key + prev_token_id.to_bytes(8, "little")
seed_bytes = hashlib.sha256(payload).digest()[:8]
seed = int.from_bytes(seed_bytes, "little")
rng = np.random.default_rng(seed)
green_size = int(vocab_size * gamma)
green_ids = rng.choice(vocab_size, size=green_size, replace=False)
return set(int(x) for x in green_ids)
def detect_watermark(
token_ids: List[int],
vocab_size: int,
gamma: float = 0.5,
secret_key: bytes = b"vi-edu-demo-key",
z_threshold: float = 4.0,
) -> Dict[str, float | bool | str]:
"""Phát hiện watermark trong một chuỗi token."""
if len(token_ids) < 2:
raise ValueError("Cần ít nhất 2 token để phát hiện watermark.")
green_count = 0
total = len(token_ids) - 1 # Bỏ token đầu (không có prev)
for i in range(1, len(token_ids)):
prev = token_ids[i - 1]
green_list = _green_list_for_context(
prev, vocab_size, gamma, secret_key,
)
if token_ids[i] in green_list:
green_count += 1
# z-score test
expected = total * gamma
variance = total * gamma * (1 - gamma)
z = (green_count - expected) / np.sqrt(variance)
# p-value xấp xỉ (1-tail)
from scipy.stats import norm
p_value = 1.0 - norm.cdf(z)
confidence = (
"cao" if z > 6.0
else "vừa" if z > z_threshold
else "thấp"
)
return {
"z_score": float(z),
"p_value": float(p_value),
"green_ratio": green_count / total,
"is_ai": bool(z > z_threshold),
"confidence": confidence,
"total_tokens": total,
}
# ─────────────────────────────────────────────────────────
# Ví dụ sử dụng với tokenizer thực tế
# ─────────────────────────────────────────────────────────
if __name__ == "__main__":
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("vinai/PhoGPT-7B5")
text = open("suspicious_essay.txt", encoding="utf-8").read()
ids = tok.encode(text)
result = detect_watermark(
token_ids=ids,
vocab_size=tok.vocab_size,
gamma=0.5,
)
print(f"Tổng tokens: {result['total_tokens']}")
print(f"Tỷ lệ green: {result['green_ratio']:.3f}")
print(f"z-score: {result['z_score']:.2f}")
print(f"p-value: {result['p_value']:.2e}")
print(f"Phán đoán: {'AI' if result['is_ai'] else 'Người/mơ hồ'}")
print(f"Độ tin cậy: {result['confidence']}")"""
Sinh văn bản có watermark bằng logit processor.
Tương thích với Hugging Face Transformers.
"""
import torch
from transformers import LogitsProcessor
class WatermarkLogitsProcessor(LogitsProcessor):
"""Cộng delta vào logits của green tokens trước softmax.
Triển khai theo Kirchenbauer et al., 2023.
"""
def __init__(
self,
vocab_size: int,
gamma: float = 0.5,
delta: float = 2.0,
secret_key: bytes = b"vi-edu-demo-key",
):
self.vocab_size = vocab_size
self.gamma = gamma
self.delta = delta
self.secret_key = secret_key
self._cache: dict[int, torch.Tensor] = {}
def _green_mask(self, prev_token: int, device: torch.device) -> torch.Tensor:
"""Trả về mask nhị phân (vocab_size,) — 1 cho green, 0 cho red."""
if prev_token in self._cache:
return self._cache[prev_token].to(device)
import hashlib, numpy as np
payload = self.secret_key + int(prev_token).to_bytes(8, "little")
seed = int.from_bytes(
hashlib.sha256(payload).digest()[:8], "little",
)
rng = np.random.default_rng(seed)
size = int(self.vocab_size * self.gamma)
green = rng.choice(self.vocab_size, size=size, replace=False)
mask = torch.zeros(self.vocab_size)
mask[green] = 1.0
self._cache[prev_token] = mask
return mask.to(device)
def __call__(
self,
input_ids: torch.LongTensor,
scores: torch.FloatTensor,
) -> torch.FloatTensor:
# input_ids: (batch, seq_len) — seq_len có thể khác nhau giữa batch
# scores: (batch, vocab_size) — logits cho token tiếp theo
for i in range(input_ids.shape[0]):
prev = int(input_ids[i, -1].item())
mask = self._green_mask(prev, scores.device)
scores[i] = scores[i] + self.delta * mask
return scores
# ─────────────────────────────────────────────────────────
# Ví dụ: watermark hoá PhoGPT tiếng Việt
# ─────────────────────────────────────────────────────────
if __name__ == "__main__":
from transformers import AutoModelForCausalLM, AutoTokenizer
name = "vinai/PhoGPT-7B5-Instruct"
tok = AutoTokenizer.from_pretrained(name)
model = AutoModelForCausalLM.from_pretrained(name, torch_dtype=torch.float16)
processor = WatermarkLogitsProcessor(
vocab_size=tok.vocab_size, gamma=0.5, delta=2.0,
)
inputs = tok("Giới thiệu về vịnh Hạ Long:", return_tensors="pt")
out = model.generate(
**inputs,
max_new_tokens=200,
logits_processor=[processor],
do_sample=True,
top_p=0.9,
)
print(tok.decode(out[0], skip_special_tokens=True))Khoá bí mật: Bảo vệ secret_key như API key. Rò rỉ khoá = kẻ tấn công có thể giả mạo watermark hoặc xoá chính xác.
Phiên bản tokenizer: Phát hiện cần đúng tokenizer của mô hình phát hành. Fine-tune đổi tokenizer → phá watermark.
Multi-language: Với văn bản song ngữ (Việt + Anh trộn lẫn), cần green list chung hoặc phát hiện theo từng đoạn ngôn ngữ.
Giáo dục đại học: Phát hiện bài luận do AI viết. Đại học quốc gia TP.HCM đã thử nghiệm hệ thống tương tự Turnitin Originality, nhưng hiệu quả với tiếng Việt còn hạn chế do tokenizer và corpus training.
Báo chí & tin tức: Ghi nhãn bài viết do AI tạo trên các tờ báo điện tử để chống tin giả. Các nền tảng như VNExpress, Zing cần pipeline kiểm duyệt nội dung tự động trước khi xuất bản.
Pháp luật & chính sách: EU AI Act (điều 50) yêu cầu ghi nhãn "nội dung sinh bởi AI". Nghị định 53/2022/NĐ-CP về an ninh mạng của Việt Nam đang được cập nhật để bao gồm nội dung tổng hợp — watermark là giải pháp kỹ thuật khả thi.
Mạng xã hội: Facebook, TikTok đang thí điểm gắn nhãn "AI-generated". Google SynthID cung cấp watermark cho ảnh, văn bản, audio do Google AI (Gemini, Imagen) tạo ra.
Pháp y số (digital forensics): Cơ quan điều tra có thể dùng watermark làm bằng chứng truy nguyên nguồn gốc nội dung giả mạo, lừa đảo.
- Watermark văn bản chia từ vựng thành green/red list sinh từ hash(token trước), cộng bias delta vào logits green khi sinh. Văn bản có watermark có tỷ lệ green cao bất thường.
- Phát hiện bằng z-score one-proportion test: z = (|g| - γT) / √(γ(1-γ)T). z > 4 ⇔ p < 3×10⁻⁵ — gần như chắc chắn có watermark.
- Paraphrase là tấn công hiệu quả nhất: viết lại 30-40% câu có thể giảm z-score đáng kể. Giải pháp lâu dài là semantic watermark và content provenance (C2PA).
- Tiếng Việt bị BPE fragment → effective vocab nhỏ → cần đoạn dài hơn (200-300 tokens) và chịu watermark yếu hơn tương đối so với tiếng Anh.
- Watermark cryptographic (SynthID-Text, Aaronson) dùng PRF với khoá bí mật, cho phép distortion-free và chống tấn công mạnh hơn, nhưng cần quản lý khoá.
- Ứng dụng: phát hiện bài AI trong giáo dục, ghi nhãn tin tức chống tin giả, tuân thủ EU AI Act và khung pháp lý nội dung số của Việt Nam.
Kiểm tra hiểu biết
Watermark cho văn bản AI (Kirchenbauer et al., 2023) hoạt động cốt lõi bằng cách nào?