Atom 002 说说Claude加的水印
昨天刚好看了Sebastian Raschka讲的关于Claude的水印原理:How Claude's Text Watermarking Works,简单写写笔记。对LLM感兴趣可以去看看他的视频,这个德国小哥很喜欢from scratch。
前不久看到这个加水印的消息时,我一直以为是对文件层进行水印嵌入,今天看了一下才发现不是这么一回事。这个水印其实是藏在“随机选词”里的。Claude借鉴的是Google DeepMind 2024 年发表在Nature上的SynthID-Text 的一个版本。
在说水印前,可能需要先提一下LLM的文字生成原理,如果想了解更多,推荐AK的视频:
Deep Dive into LLMs like ChatGPT
文字生成是个带着概率偏好的接龙游戏
把LLM生成文本想象成吐字机,每次吐一个字/token,当文字tokenization后,丢入LLM,LLM会对下一个字的token进行预测,生成下一个词的概率分布。

对于下一个字的选择,有不同的选择策略,比如贪婪解码(Greedy decoding),Top-K,Top-P等。Greedy很好理解,选最好的,但是这会让LLM生成的语言死板不像人说的,所以通常会在这里加入一些随机性,比如Top-K和Top-P。Top-K是通过选择前K个候选词做个新池再随机抽样。Top-P则是根据累积概率P建新池子再随机抽样。

水印的核心机制
既然选词是一个随机采样的过程,那能不能把这个“随机性”变成一种带秘密规律的随机性呢?Claude的水印就是利用这里。
在使用Numpy时,为了让一些随机过程具备复现性,我们通常会通过设置随机数种子,这里也是类似,当设置随机数种子时,就会让LLM在选择下一个token时有个偏好。



这个水印就是通过生成一个密钥,动态生成随机数种子,让LLM生成的文本选择有个偏好。所以消除水印的方法就是在这些水印关键词上做调整,但是因为没有密钥,我们并不知道哪些词是水印关键词。Sebastian也说了,或许A社会通过开放这些密钥的调用API来提供检测。



那么这个水印是怎么嵌入的呢?后续又如何检测?如果嵌入的水印最后还需要对整个文本重新进行检测和扫描,成本不小。这里就引出了一个锦标赛采样(Tournament Sampling)的评分函数方法。其实就是在生成文本的时候,人为制造一些只有密钥持有者才知道的统计偏差,检测时再把这个偏差找出来。
模型在采样时会使用一组随机函数(如G1,G2,G3),这几个函数对于任意的token都会生成0或1。借助这几个随机函数,根据密钥和当前上下文,为候选token生成一个唯一的“签名”。作为候选token在当前上下文下的秘密评分向量。所以,对于同一个token,在不同上下文语境下,这个签名编码会不同。
然后将候选token两两配对,模仿体育比赛的淘汰制。每一轮比赛中,根据当前的随机函数输出结果来决定哪一个token胜出,经过多轮“锦标赛”,最终选出一个胜出的token作为下一个生成的输出token。
这个过程就会让最终出现的token比自然文本更容易具有较多的 1,从而产生一定的统计偏移。


到了文本检测的时候,在密钥,上下文以及最终token(水印位置)计算那个由多个G函数生成的01向量编码,分数超过一定阈值就被认为是有水印标记的。

那为什么这种水印嵌入方法不会影响模型的生成质量呢?关键点在于SynthID-Text 不是为了水印把模型喜欢的词换成另一个词,而是重新组织了模型本来就需要的随机采样,将水印信号藏在随机性的结构里。它只是利用原来「怎么选都差不多」的地方。把本来就存在的随机选择改成一种由密钥控制的随机选择。