概述

随着 LLM 生成内容的大量涌现,如何区分人类与 AI 生成的文本成为版权保护、学术诚信和内容安全的关键挑战。LLM 水印技术通过在生成过程中嵌入统计信号,使后续检测能够判断文本是否由特定模型生成。

2025-2026 年,Google DeepMind 的 SynthID-Text 和 OpenAI 的文本分类器代表了两大技术路线:嵌入水印 vs 后置检测。


一、水印技术分类

类型 原理 优势 局限
基于词表分割 将词表分为绿/绿名单,偏好绿名单词 理论保证、可证明检测 对改写攻击敏感
基于概率偏移 微调采样概率分布 对文本质量影响小 检测需要较长文本
基于语义 嵌入语义级特征 抗改写较强 实现复杂
基于格式 标点、空格等微调 零质量损失 易被格式化清除

二、核心技术:词表分割水印(KGW)

由 Kirchenbauer 等人 2023 年提出,是当前最主流的 LLM 水印方案。

2.1 算法原理

水印嵌入过程:
1. 将词表 V 分为绿名单 G 和红名单 R(基于哈希)
2. 在生成时,对绿名单词的 logit 加 δ
3. 从调整后的分布中采样

水印检测过程:
1. 统计文本中绿名单词的比例
2. 使用 z 检验判断是否超过统计阈值

2.2 代码实现

import torch
import torch.nn.functional as F
from hashlib import sha256

class KGWWatermark:
    """
    Kirchenbauer et al. 水印方案实现
    """

    def __init__(
        self,
        vocab_size: int,
        green_list_ratio: float = 0.25,
        delta: float = 2.0,
        seeding_scheme: str = "simple_1",
        hash_key: int = 15485863,  # 第100万个质数
    ):
        self.vocab_size = vocab_size
        self.green_list_ratio = green_list_ratio
        self.green_list_size = int(vocab_size * green_list_ratio)
        self.delta = delta
        self.seeding_scheme = seeding_scheme
        self.hash_key = hash_key

    def _get_green_list(self, prev_token: int) -> set:
        """
        基于前一个 token 生成绿名单
        使用哈希确保伪随机但确定性
        """
        h = sha256(f"{self.hash_key}{prev_token}".encode()).hexdigest()
        # 用哈希值生成伪随机序列
        torch.manual_seed(int(h[:8], 16))
        perm = torch.randperm(self.vocab_size)
        return set(perm[:self.green_list_size].tolist())

    def watermark_logits(self, input_ids: torch.Tensor, logits: torch.Tensor) -> torch.Tensor:
        """
        在 logits 上添加水印
        input_ids: [batch, seq_len] 已生成的 token
        logits: [batch, seq_len, vocab_size] 模型输出
        """
        batch_size, seq_len, _ = logits.shape
        watermarked_logits = logits.clone()

        for b in range(batch_size):
            for t in range(seq_len):
                if t == 0:
                    continue  # 第一个 token 不加水印

                prev_token = input_ids[b, t - 1].item()
                green_list = self._get_green_list(prev_token)

                # 对绿名单 token 的 logit 加 delta
                for token_id in green_list:
                    watermarked_logits[b, t, token_id] += self.delta

        return watermarked_logits

    def detect(self, text_tokens: torch.Tensor, z_threshold: float = 4.0) -> dict:
        """
        检测文本中是否包含水印
        """
        n = len(text_tokens)
        if n < 10:
            return {"detected": False, "reason": "文本过短"}

        green_count = 0
        for i in range(1, n):
            prev_token = text_tokens[i - 1].item()
            curr_token = text_tokens[i].item()
            green_list = self._get_green_list(prev_token)
            if curr_token in green_list:
                green_count += 1

        # z 检验
        expected = self.green_list_ratio * (n - 1)
        std = (self.green_list_ratio * (1 - self.green_list_ratio) * (n - 1)) ** 0.5
        z_score = (green_count - expected) / std if std > 0 else 0

        return {
            "detected": z_score > z_threshold,
            "z_score": z_score,
            "green_count": green_count,
            "total_tokens": n - 1,
            "green_ratio": green_count / (n - 1),
            "threshold": z_threshold,
        }


# 使用示例
watermarker = KGWWatermark(vocab_size=50257, green_list_ratio=0.25, delta=2.0)

# 生成时加水印
# watermarked_logits = watermarker.watermark_logits(input_ids, logits)
# next_token = sample_from(watermarked_logits)

# 检测时
# result = watermarker.detect(text_token_ids, z_threshold=4.0)
# print(f"水印检测: {result['detected']}, z-score: {result['z_score']:.2f}")

三、SynthID-Text 方案

Google DeepMind 在 2024-2025 年推出的 SynthID-Text 采用更精细的概率偏移方案:

3.1 核心改进

特性 KGW SynthID-Text
水印强度 固定 δ 自适应调整
检测方式 z 检验 多层神经网络
文本质量影响 中等
抗攻击性
开源状态 开源 部分开源

3.2 概率偏移公式

def synthid_watermark_sampling(logits, prev_token, watermark_key, gamma=0.5):
    """
    SynthID-Text 风格的水印采样
    使用 T 函数调整概率分布
    """
    # 1. 计算原始概率
    probs = F.softmax(logits, dim=-1)

    # 2. 生成基于前一个 token 的水印掩码
    torch.manual_seed(hash((watermark_key, prev_token)) % (2**32))
    mask = torch.rand(len(logits)) < gamma  # 选中 gamma 比例的 token

    # 3. 对选中 token 进行概率提升
    T = 0.5  # 偏移强度
    adjusted_probs = probs.clone()
    adjusted_probs[mask] = probs[mask] * (1 + T)

    # 4. 重新归一化
    adjusted_probs = adjusted_probs / adjusted_probs.sum()

    return adjusted_probs

四、抗攻击性分析

4.1 常见攻击方式

攻击方式 描述 KGW 抗性 SynthID 抗性
截断 删除文本首尾
改写(Paraphrase) 用另一个 LLM 改写
翻译攻击 翻译到其他语言再翻译回来
Token 替换 同义词替换
拼写错误注入 随机插入拼写错误
格式化 去除格式

4.2 改写攻击模拟

def simulate_paraphrase_attack(original_text, paraphrase_model, n_iterations=3):
    """
    模拟多轮改写攻击,测试水印残留
    """
    current_text = original_text
    detection_results = []

    for i in range(n_iterations):
        # 检测水印
        tokens = tokenize(current_text)
        result = watermarker.detect(tokens)
        detection_results.append({
            "iteration": i,
            "text_length": len(current_text),
            "z_score": result["z_score"],
            "detected": result["detected"],
        })

        # 改写
        current_text = paraphrase_model.rewrite(current_text)

    return detection_results

五、多模态水印

5.1 图像水印

# AI 生成图像的水印方案对比
image_watermark_methods = {
    "DCT域水印": {
        "原理": "在离散余弦变换域中嵌入信息",
        "容量": "中等",
        "鲁棒性": "高(抗压缩)",
        "代表": "Stable Signature (Meta, 2023)",
    },
    "扩散模型水印": {
        "原理": "在扩散过程中微调模型权重嵌入水印",
        "容量": "低",
        "鲁棒性": "非常高",
        "代表": "Tree-Ring Watermark (2023)",
    },
    "频域水印": {
        "原理": "在频域中嵌入伪随机模式",
        "容量": "中等",
        "鲁棒性": "中-高",
        "代表": "SynthID-Image (Google, 2024)",
    },
}

5.2 音频水印

# AudioSeal (Meta, 2024) 原理
audio_watermark_pipeline = """
AI 生成音频
    ├─→ 水印编码器(神经网络)
    │       │
    │       ▼
    │   水印音频(不可感知的差异)
    └─→ 水印检测器
        检测结果 + 定位(时间戳级别)
"""

六、检测系统架构

class WatermarkDetectionSystem:
    """
    多层水印检测系统
    """

    def __init__(self):
        self.detectors = {
            "kgw": KGWDetector(),
            "synthid": SynthIDDetector(),
            "statistical": StatisticalDetector(),
            "classifier": ClassifierDetector(),
        }

    def detect(self, text: str) -> dict:
        """
        多检测器融合
        """
        results = {}

        for name, detector in self.detectors.items():
            try:
                result = detector.detect(text)
                results[name] = result
            except Exception as e:
                results[name] = {"error": str(e)}

        # 融合决策
        scores = [r.get("confidence", 0) for r in results.values() if "error" not in r]
        avg_confidence = sum(scores) / len(scores) if scores else 0

        return {
            "is_ai_generated": avg_confidence > 0.7,
            "confidence": avg_confidence,
            "individual_results": results,
            "text_length": len(text),
            "recommendation": self._get_recommendation(avg_confidence),
        }

    def _get_recommendation(self, confidence: float) -> str:
        if confidence > 0.9:
            return "极高置信度 AI 生成,建议标记"
        elif confidence > 0.7:
            return "可能为 AI 生成,建议人工复核"
        elif confidence > 0.4:
            return "不确定,建议进一步分析"
        else:
            return "可能为人类撰写"

七、合规与政策

地区 政策 水印要求
欧盟 AI Act (2024) 强制要求 AI 生成内容标注
中国 《生成式 AI 服务管理暂行办法》 要求标识 AI 生成内容
美国 加州 AB 2655 (2024) 要求 AI 生成政治广告标注
英国 Online Safety Act 平台需标注 AI 内容

参考


加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。