Anthropic将在AI生成文本中嵌入水印!难道是用我之前写的这个技术?

这两天在网上看到一个新闻,说 Anthropic 计划推出一种隐形水印技术

核心目的就是为了识别由AI生成的文本内容

难道那边也有要求?公众号发AI生成的内容得标注一下子?哈哈!

重点是哈:Anthropic 表示,自8月2日及之后发布的模型起,该公司会在 Claude AI 生成的文本中直接嵌入人类无法察觉、仅机器可识别的水印。

人类肉眼看不到该水印,不会降低文本质量与可读性。

即便复制粘贴文本,水印也会随之保留,部分轻度编辑操作也无法清除水印。

为啥我隐隐感觉这个表述这么的熟悉?

我之前是不是写过这个技术?

我终于想起来了,我在今年3月写过一篇文章,主要说的是前端零宽隐形水印。

详见:# 别再用显性水印!前端零宽隐形水印,实现内容溯源级版权保护,已封装复制即用

当时我写这篇文章的时候其实用的表述和这个差不多,前端零宽字符隐形水印的核心优势就是:

肉眼完全不可见、复制粘贴不丢失。

当然,咱们这里不 YY 哈!

人家 Anthropic 用的不一定就是这个技术,只是说从"效果描述"上和零宽字符的效果描述差不多。

对于"肉眼不可见,但机器可识别"这种技术方案,其实除了零宽字符还有一种更高明的方案 ------ LLM 统计语义水印

这种技术详细方案可以见《Kirchenbauer 2023 KGW 算法》这篇论文。

简单理解这种技术:不插入任何看不见特殊字符,不修改字面文字。

在模型生成文字的选词概率 里面埋统计学指纹,人读完全看不出差别。

但是用专用算法做统计检验,可以判断该文本是否由该大模型生成。

他的核心原理是通过 KGW 红绿列表算法,选词的时候生成的水印。

大模型是自回归生成,根据上文,输出下一个 token(词片段)的概率分布,从里面挑词输出。

而统计水印就是在每一步选词的时候,进行轻微的干预,让选词的概率偏向某一种,人为制造出统计学偏差


咱们打个比方,类似于,我们人眼现在看豆子都一样,我们分不清楚绿豆和红豆的颜色,我们认为他们都是豆子。

本来大模型挑豆子就是随机的,可能挑红的,也可能挑绿的,没啥规律。

现在通过统计水印干预后,大模型挑的大多数都是红的,尽量避免,或者完全不挑绿的。

但是人眼看不出区别,机器可以分辨。

生成阶段(埋水印)

  1. 拿到已经生成的上文 token 序列,用密钥 + 哈希函数算出一个随机种子 K
  2. 利用这个种子 K,把模型全部词汇表随机切分成两类:绿名单 (Green)、红名单 (Red)
  3. 不禁止红名单,只给绿名单所有 token 的 logit(分数)统一加一个小偏置,从而提高选中绿名单词语的概率
  4. 模型继续正常采样输出下一个词,循环往复生成完整文本。

这种轻微偏差的运算下,模型输出出来的内容,人是看不出区别的。

比方说,原本大模型的输出为:今天天气晴朗。

在统计水印的干预下,变成了:今天天气明媚。

在人看来这两个词语没什么太大的区别,语序/语义几乎完全一致。

但是机器可以识别出来,这两个词一个在红名单,一个在绿名单。

统计水印就是靠这种选词偏好埋的标记,不是调换词语先后顺序。

注意:这里不会强制必须选绿词,只是提高概率

强制选绿可能会导致输出的内容存在问题,所以这里只是概率。

这样输出的内容自然绿词偏多,红词偏少,机器就可以识别了。

检测阶段(提取水印)

拿到一段文本,不知道其生成过程,只知道公开检测密钥。

  1. 逐 token,复现当初的哈希 + 种子逻辑,重新还原每一步的绿 / 红名单;
  2. 统计整篇文本里面,实际出现多少绿名单 token;
  3. 统计学 Z‑检验,计算 p 值:判断 "绿词占比偏高" 是随机巧合,还是水印干预造成的。

判断如果 p 值足够小,极大概率是大模型生成的。

判断如果 p 值不够小,则有可能是人写的,或者人改编的。


明白了 LLM 统计水印的原理你就明白了为什么这个水印是有局限性的:

已经写好的文本,统计水印无能为力。

因为统计水印本身通过选词概率进行识别,而已经写好的文本不存在任何选词概率了,固定死了。

同时如果你对AI生成的文本进行大篇幅的修改,同义词替换等操作,大量的 Token 被替换,这也无法正确识别。

还有就是非常短的一句话,或者是一个段落,这样也很难正确识别,容易出现误判。

另外也正是因为 LLM 统计水印这种偏差影响,会导致大模型输出的内容可能存在负面影响

毕竟不是所有的词语、释意都有对应的词来替换,替换后可能会让人微微的感觉语序、语义存在问题。

英文或许还好,但是中文这个问题尤其明显。

中文的语义实在是太复杂了,不通过一长段的上下文你很难读懂这部分到底在表达什么意思。(梦回文言文阅读理解)

这里简单给大家上个Demo,可以尝尝LLM统计水印的效果:

py 复制代码
import hashlib
import random
from scipy.stats import norm

# ====================== KGW 水印模拟 Demo ======================
# 1. 模拟词汇表,模拟大模型的token词表
VOCAB = [
    "晴天", "明媚", "下雨", "潮湿", "刮风", "寒冷", "温暖", "凉爽",
    "大海", "高山", "森林", "河流", "城市", "夜晚", "清晨", "黄昏"
]

WATERMARK_SEED = 12345  # 水印密钥,生成与检测必须相同
GAMMA = 0.5  # 红绿划分比例,绿词占词表50%
DELTA = 2.0  # 给绿词加分的偏置,越大水印信号越强


def get_green_red_list(context_tokens: list, seed: int):
    """
    根据上文context + seed哈希,生成本步的绿名单、红名单(KGW核心)
    """
    ctx_str = "|".join(context_tokens)
    h = hashlib.sha256(f"{ctx_str}_{seed}".encode()).hexdigest()
    rand = random.Random(int(h, 16) % (2**32))
    shuffled_vocab = VOCAB.copy()
    rand.shuffle(shuffled_vocab)
    split_idx = int(len(shuffled_vocab) * GAMMA)
    green = set(shuffled_vocab[:split_idx])
    red = set(shuffled_vocab[split_idx:])
    return green, red


def watermarked_generate(max_len=12):
    """模拟带水印的文本生成:每一步优先抬高绿词概率"""
    output = []
    for _ in range(max_len):
        green_set, red_set = get_green_red_list(output, WATERMARK_SEED)
        logits = {}
        for word in VOCAB:
            base_score = random.uniform(0, 1)
            if word in green_set:
                base_score += DELTA  # 绿词加分,提高被选中概率
            logits[word] = base_score
        # 选分数最高的词输出
        next_word = max(logits.items(), key=lambda x: x[1])[0]
        output.append(next_word)
    return " ".join(output), output


def detect_watermark(text_tokens: list, seed: int):
    """
    KGW水印检测:统计绿词占比,计算Z‑score,返回z值,p值
    z越高代表水印信号越强,一般 z>3 认为大概率存在水印
    """
    num_green = 0
    total = len(text_tokens)
    for idx in range(total):
        context = text_tokens[:idx]
        green, _ = get_green_red_list(context, seed)
        token = text_tokens[idx]
        if token in green:
            num_green += 1

    expected = total * GAMMA
    variance = total * GAMMA * (1 - GAMMA)
    if variance <= 0:
        return {"z_score": 0, "num_green": num_green, "total": total}
    z_score = (num_green - expected) / (variance ** 0.5)
    return {
        "z_score": z_score,
        "num_green": num_green,
        "total_tokens": total
    }


if __name__ == "__main__":
    # 生成一段带水印文本
    gen_text, tokens = watermarked_generate(max_len=14)
    print(f"【生成带水印文本】:{gen_text}")
    res = detect_watermark(tokens, WATERMARK_SEED)
    print(f"【水印检测结果】{res}")
    print("\n说明:z_score >3,代表水印信号显著;z接近0无水印")

    # 测试:打乱改写一部分token,模拟人工改写破坏水印
    import copy
    tokens_broken = copy.deepcopy(tokens)
    for i in range(4):
        tokens_broken[i] = random.choice(VOCAB)
    res_broken = detect_watermark(tokens_broken, WATERMARK_SEED)
    print(f"\n【改写破坏后检测结果】{res_broken}")

# 运行输出示例
# 【生成带水印文本】:明媚 温暖 大海 晴天 黄昏 凉爽 清晨 高山 明媚 温暖 大海 晴天 黄昏 凉爽
# 【水印检测结果】{'z_score': 4.123..., 'num_green':11, 'total_tokens':14}
# 说明:z_score >3,代表水印信号显著;z 接近 0 无水印
# 【改写破坏后检测结果】{'z_score': 0.412..., 'num_green':8, 'total_tokens':14}

或许我猜 Anthropic 是用的这两种方案的结合?

回头大家可以拿 Anthropic 生成出来的文本检测试试,据说老美那边已经有人提前布局"去除 Anthropic 水印"产业了。

合着世界上最锋利的矛和世界上最坚固的盾?

相关推荐
Asize1 小时前
前端接口工程:axios + mock,前端不再傻等后端
前端·javascript
结网的兔子1 小时前
【前端开发】UniApp 项目地图选型与Web-APP跨端迁移方案
前端·uni-app
Canace1 小时前
给 Claude 一个链接,它真的读了原文吗
前端·人工智能·ai编程
爱丶不疚1 小时前
Electron net 模块你可以没用过,但不能不知道
前端·electron
cidy_981 小时前
React + Ant Design 通用企业数据统计模块实战
前端
渣波1 小时前
React 性能优化与状态管理双雄:useMemo 与 useReducer 深度解析
前端·javascript
Maxkim1 小时前
把智能体塞进浏览器侧边栏:我在 MV3 里踩的 5 个坑
前端·后端
渣波1 小时前
告别 LLM 幻觉:用 Harness 工程化思维打造生产级 AI 应用
前端·javascript
神奇霸王龙1 小时前
V4-Flash 公测:Agent 智能路由白菜化
ai·aigc·agent·ai编程·ai写作·deepseek