目录
第一章 介绍
1.1 符号与语言模型基础
1. 先懂 “词汇表(Vocabulary)”
语言模型得先有个 “词库”,叫 词汇表 ,里面存的不只是完整单词,像 “-ing” “##tion”(这种拆分的词片段)也包含,这些统统叫 “标记(tokens)” (我的理解就是:一个词算一个token)。常见的词汇表,少则存 5 万个标记,多的话更多,就像手机输入法的词库,存着你可能输入的各种内容。
2. 理解 “标记序列”
有了词库,语言模型要处理 标记序列 ,比如一段文本会被拆成 这样的标记串(T 是序列总长度 )。这里 “带负号的索引”(像 ),你可以简单理解成 “提示内容” ,就像你给朋友发消息:“周末去___” ,“周末去” 就是让朋友接着填的 “提示”;而
就是模型顺着提示生成的回复,比如朋友回 “爬山”“看电影” 这些内容。
3. 语言模型咋预测下一个词?
语言模型(LM)的核心活儿是 “猜下一个词” ,它像个智能接龙选手。它的工作方式是:先拿到 “提示 + 已经生成的内容”(也就是 是已经生成到第几个标记 ),然后用神经网络算一算,输出一个 “对数几率(logits)” 向量 —— 可以理解成给词汇表里每个词打个 “潜力分”,看谁最该当 “下一个词”。
接着,这个 “潜力分” 会被 softmax 算子 加工,变成 “概率分布” ,就像把 “潜力分” 换算成 “当选概率”,词汇表里每个词都有对应的概率,总和是 100% 。
最后,选下一个词有不同方法:
- 多项式采样 :类似抽奖,按概率随机选,可能选到不太常见但合理的词,让生成内容更灵活;
- 贪心采样(贪心解码) :直接选概率最高的那个词,简单粗暴,追求 “最可能的延续”;
另外,还有 束搜索(beam search) 这种 “聪明玩法” ,它不着急选单个词,而是同时留几个 “候选序列”,比比谁整体得分高,再选最好的,有点像选路线,多规划几条,挑最终最优的。
1.2 低熵序列水印添加的难点
1.低熵序列的特性与判断困境
先看例子,文中给了两类标记序列:
这些序列属于 低熵序列 ,核心特点是 “前序标记强约束后续内容” 。比如看到 The quick brown ,熟悉英语的人大概率能猜到后续是 fox jumps over the lazy dog ;代码里了 for(i=0
,懂编程的基本知道要循环做累加操作。这种强关联性下,人机生成的边界极模糊 —— 人类凭知识 / 习惯能写出,语言模型学了大量文本 / 代码也能生成,所以 “到底谁写的” 很难判断。
- 自然语言侧:The quick brown fox jumps over the lazy dog(经典英语 pangram ,26 个字母全涵盖 )
- 代码侧:
for(i=0;i<n;i++) sum+=array[i]
(基础的数组遍历求和代码逻辑 )
2.entropy
所谓的entropy(熵)是一个非常重要的概念,来自信息论(Information Theory),本质上是用来衡量“不确定性”或“信息量”的。通俗理解 Entropy(熵)。熵 = 不确定性 = 随机程度
举例:如果一个模型输出的概率分布是:{"猫": 0.33, "狗": 0.33, "鸟": 0.34}→ 表示模型非常不确定 → 熵高
如果输出是:{"猫": 0.98, "狗": 0.01, "鸟": 0.01}→ 模型很有把握 → 熵低
3.低熵对水印技术的双重挑战
低熵序列给 “水印添加” 挖了两个坑:
人机补全趋同,水印难区分来源
低熵场景里,人类和模型的 “续写答案” 高度重合(甚至完全一样 )。比如面对 The quick brown ,人写 fox… ,模型训练时学过这个经典用法,也会输出 fox… 。这时候,想靠 “水印特征” 区分人机生成,根本无从下手 —— 内容本身几乎没差异,水印标记也嵌不进去(因为大家写得都一样 )。水印嵌入易破坏文本质量
低熵序列的生成逻辑很 “死”,每个标记的选择都被前序牢牢锁死。要是强行加水印(比如微调个别标记 ),很容易打破这种约束,让文本变 “畸形”:
- 自然语言里,把 fox 换成生僻词,句子直接不通;
- 代码里,改了循环变量或累加逻辑,程序直接报错。
这种 “牵一发动全身” 的特性,导致水印添加的 “修改空间极小”,稍微动一下,文本 / 代码就从 “通顺可用” 变成 “狗屁不通”,水印还没起到标识作用,先把内容毁了。
第二章 一个简单的概念验证
我们先介绍算法 1 里的一种简单 “硬红名单” 水印方法,它易于分析、检测,且难以移除。不过,这种方法的简洁性是以牺牲低熵序列的生成质量为代价的。后续我们会探讨更复杂的策略。
算法 1:带硬红名单的文本生成
- 输入:提示信息 :
(这部分相当于用户输入信息,例如:今天天气)
- 步骤(循环执行,
): (这里的t可以理解为输出的每一个token的下标)
- 将语言模型应用于之前的标记 ,得到词汇表上的概率向量
。
- 计算标记
的哈希值,并用其作为随机数生成器的种子 。
- 基于这个种子,把词汇表随机分成大小相等的 “绿名单” G 和 “红名单” R 。
- 从 G 中采样生成
,绝不从红名单 R 里选标记
- 将语言模型应用于之前的标记 ,得到词汇表上的概率向量
接下来对算法1进行解释:
1. 先理解 “语言模型的基础操作”(步骤1)
语言模型本来的工作是:给一段 “之前的文本(也就是这里的 ) ,可以理解成提示内容,比如你说 “今天天气” )”,它会算出每个词出现在下一个位置的概率 。
比如你给模型提示 “今天天气”,语言模型会分析词汇表(像 “好”“不错”“晴朗” ),给每个词打个 “概率分”:“好” 可能 30% 概率,“晴朗” 可能 20% ,这些概率凑一起就是 概率向量,这一步是语言模型的 “常规操作”,先算出所有词的出现可能性。
2. “哈希值 + 随机数种子”(步骤2)
接下来,算法要搞 “水印”,得让生成的文本有独特标记。这时候用到 “哈希(Hash)” 和 “随机数种子” :
- 哈希(Hash):可以理解成 “加密小工具” 。把之前生成的最后一个词
(比如上一步生成了 “好” )丢进去,它会输出一串固定的、看似随机的数字(像把 “好” 变成 “a1b2c3” ),这串数字就是 哈希值 。
- 随机数种子:用刚才的哈希值当 “种子”,就像给随机数生成器定一个 “初始密码” 。有了这个密码,每次用它生成随机数,结果都是一样的(保证后续能 “重现” 这个过程,方便检测水印 。)注意:指的是同一个哈希值得到的随机数种子是一样的,而不是每次生成新的词时的随机数是一样的,不要混淆。
3. “红绿名单”:限制模型选词(步骤3)
有了种子,下一步是 把词汇表拆成 “绿名单 G” 和 “红名单 R” :
- 词汇表就像个 “词库大仓库”,里面存着所有可能的词(比如 “好”“坏”“美丽” )。用种子启动的随机数生成器,会把仓库里的词 “随机分组”,比如一半放进 “绿名单 G”(允许选的词 ),另一半放进 “红名单 R”(禁止选的词 ),而且两组数量一样多。
然后,模型生成下一个词的时候,只能从绿名单 G 里选 ,红名单 R 里的词再怎么概率高,也不能选。相当于给模型戴了个 “选词枷锁”,强制它只能从特定范围里挑词,这样生成的文本就被悄悄打上了 “水印标记”(因为正常人写东西不会这么被限制选词,正常人选词就是从“词汇大仓库”中选,后续检测时,看是否有这种 “强制选词痕迹”,就能判断是不是模型生成的 )。(步骤4)
可能你还是听不懂,那么我就举个完整例子串一遍逻辑
假设你要生成文本,提示是 “今天天气”(对应):
- 模型先常规操作:算词汇表每个词的概率,比如 “好” 30%、“晴朗” 20%、“坏” 10%…… 得到概率向量
。
- 假设上一步生成的词
是 “好” ,对 “好” 做哈希,得到种子 “a1b2c3” 。
- 用 “a1b2c3” 当种子,随机把词汇表拆成 G(比如包含 “不错”“宜人” )和 R(比如包含 “晴朗”“坏” )。
- 模型生成下一个词 时,只能从 G 里选,比如选了 “不错” ,所以最终文本变成 “今天天气不错” 。
在这个算法里,每生成一个新的词 ,步骤 1 到 4 都会完整执行一遍 ,相当于每一步生成都要 “重新走流程”,这样才能持续给文本打上水印。
水印检测方法:
1.检测方优势
生成水印文本需要访问语言模型,但检测水印不需要。第三方只要懂 “哈希函数 + 随机数生成器” 的规则,就能复现每个词(token)对应的红名单,检查文本是否 “遵守红名单限制”,以此揪出水印。
2.原假设” 与概率博弈
原假设 :假设文本是 “自然生成的”(生成时不知道红名单规则 )。
红名单的随机性:因为红名单是随机划分的,正常人写东西,理论上约一半的词会 “违反红名单规则”(毕竟红名单随机禁了一半词,正常人不会刻意避开 )。但!带水印的文本是模型生成的,模型被强制只能选绿名单的词,所以完全不会违反红名单规则(违规次数 = 0 )。
概率碾压:如果文本是自然生成的,要 “连续 T 个词都不违反红名单规则”,概率是
。哪怕文本很短(比如 10 个词的推文 ),这个概率也极低(
);词数越多,概率越趋近于 0 。所以只要发现 “违规次数极少(甚至为 0 )”,基本能怀疑是水印文本。
3.更严谨的 “z 检验” 方法
为了让检测更靠谱,用单比例 z 检验验证原假设,步骤如下:
实际上,我们想算 “实际偏差 ÷ 标准误”(偏差是否超出合理范围):
标准差 =
所以 “偏差 ÷ 标准误” = —— 这就是 z 统计量的由来。(此处涉及统计学理论,想深入了解的可以查看相关资料)
对于Z值的解释:自然生成的文本,应该接近
(比如抛 16 次硬币,正面数大概率在 6-10 之间),对应的 z 值会很小(接近 0);但带水印的文本,
(全合规),对应的 z 值会特别大,因为偏差
远超自然文本的合理范围。
统计量定义:
- 绿名单词数
(即文本里 “遵守红名单规则” 的词数 )。
- 若
成立(自然生成 ),
的期望(平均水平)是
(因为正常人一半词违规,一半合规 ),方差是
。
- 绿名单词数
z 统计量公式:
这个公式把 “绿名单词数与期望值的偏差”,转化成标准正态分布的统计量(方便对比阈值 )。
判定逻辑: 选一个阈值(比如
) ),如果计算出的 z 超过阈值,就拒绝原假设
,判定文本是带水印的(因为自然文本几乎不可能让 z 这么大 )。
假阳性概率: 当
时,“误判自然文本为水印文本” 的概率约
(极低 )。而且只要文本有 16 个词以上(满足
时,
),就能被检测到。
水印移除难度分析
核心逻辑:z 检验让水印难移除
使用单比例 z 检验的机制,从根本上增加了水印移除的难度。以长度 的带水印序列为例,攻击者若想篡改水印,需修改序列中的标记(tokens),但存在以下约束:
标记修改的连锁效应:序列中位置 t 的标记
会决定
的红名单规则。攻击者修改
使其违反红名单规则时,极端对抗性的选择会让
也大概率违反规则。因此,修改 200 个标记,最多引发 400 次红名单规则违规(因每个修改可能连带下一个标记违规 )。
剩余绿名单标记的检测韧性:即便攻击者极端对抗,修改后仍有 600 个绿名单标记。代入 z 统计量公式计算:
对应的 p - 值约
,远低于显著性水平,水印仍能被高置信度检测到。一般而言,移除长序列水印需修改约 四分之一及以上的标记 ,成本极高。
攻击前提的关键假设
上述分析基于两个极端条件:
- 攻击者全知:假设攻击者完全知晓水印算法,且每次修改标记都能极端对抗(但这会损害文本质量,比如让内容逻辑混乱 )。
- 攻击者无知:若攻击者不了解水印算法,修改一个标记时,自身及相邻标记仅有 50% 概率落入红名单。此时修改 200 个标记,仅能期望新增 200 次红名单违规,水印更难移除。
水印算法的保密性方案
为增强水印鲁棒性,可通过 API 开放功能但对算法保密(类似 “黑盒调用” ),让攻击者难以精准篡改。
硬红名单规则的缺陷与软水印改进思路
1.硬红名单规则的弊端
硬红名单规则对低熵序列(即文本续写高度可预测的场景 )采用 “简单粗暴禁止” 的策略,虽能限制模型生成,但会破坏合理文本的自然延续 。比如在大量文本数据中,标记 “Barack” 之后几乎必然跟 “Obama”(像人名的固定搭配 ),但硬红名单可能直接把 “Obama” 列为禁止词,导致模型无法生成这类常见、合理的文本片段,牺牲了文本生成的自然性。
2.软水印规则的优化方向
更好的方案是采用 “软” 水印规则 ,核心逻辑是:
动态激活:仅对高熵文本(续写不确定性强、可隐式加水印的内容 )生效,也就是说低熵文本不使用水印。低熵序列若被包裹在 “整体高熵的文本段落” 中,段落整体仍能触发水印检测,解决前面提到的 “低熵序列难检测、难加水印” 问题(低熵序列本身难处理,但依托高熵段落的检测韧性,可间接覆盖 )。
结合束搜索解码器:把水印机制与束搜索(beam search )结合,在可能的标记序列假设空间里搜索,筛选绿名单标记密度高的候选序列。这样既能强化水印(高绿名单占比让检测更显著 ),又能通过束搜索的优化,把 “增加水印导致的文本困惑度(perplexity,即文本合理性损失 )” 降到最低,平衡水印强度与文本质量。
3.核心逻辑闭环
硬红名单的问题在于 “过度限制低熵序列,破坏自然文本”;软水印通过区分高 / 低熵场景动态处理,并结合束搜索优化,实现 “既保障水印可检测,又尽量不影响文本自然性”。本质是让水印机制更 “智能”:对难处理的低熵内容,借高熵段落间接覆盖;对生成过程,用束搜索在合理文本里强化水印特征,最终达成 “有效检测 + 低质量损失” 的平衡。
第三章 更精巧的水印方案
1.核心目标
“软” 水印的设计聚焦 差异化干预策略 :针对文本生成中不同熵值的标记(token),灵活调整干预强度。对于 高熵标记(即续写时存在多个合理选项、不确定性强的情况 ),引导语言模型优先从绿名单中选择词汇;而面对 低熵标记(续写高度确定,几乎只有唯一合理词汇可续的场景 ),则尽量减少干预,以此保障文本生成的自然流畅性,避免硬红名单方案中 “因过度限制导致文本逻辑断裂” 的问题。
2.基础概率生成逻辑
语言模型生成文本过程里,其最后一层会输出 对数几率(logits)向量 ,该向量包含了词汇表中每个词作为下一个生成词的 “原始分数” 。为将这些原始分数转化为可直接用于采样的概率分布,需经过 softmax 函数处理,得到概率向量
,转换公式为:
这一步是语言模型预测下一个词概率分布的标准流程,本质是把 logits 数值映射到[0,1]区间,且所有词的概率之和为 1 ,从而得到词汇表上各词的生成概率。
3.改进
与硬红名单方案中 “直接禁止红名单词生成” 的强干预方式不同,软水印(对应算法 2 )采用更柔性的策略 ——给绿名单词的 logits 添加常数 ,而非严格阻断红名单词的生成可能。具体来看: 在算法执行流程中,先依据前序标记生成 logits 向量
,再通过哈希前序标记获取随机种子,以此随机划分出绿名单 G 和红名单 R 。之后,对绿名单 G 内所有词对应的 logits ,统一加上常数
,相当于给这些词的 “生成优先级” 额外加分 。完成 logits 调整后,再次通过 softmax 函数转换,得到新的概率分布
,基于此分布采样生成下一个词。这种方式不是从生成源头 “封杀” 红名单词,而是通过提升绿名单词的概率占比,间接引导模型更多选择绿名单词,在保留文本生成灵活性的同时,悄然嵌入水印特征 。
简单来说,软红名单方案以 “奖励绿名单” 替代 “禁止红名单”,既达成了水印嵌入的目标,又降低了对文本自然生成逻辑的破坏,让水印更 “隐形” 地融入文本序列 。
算法 2:带软红名单的文本生成
输入:提示词; 绿名单规模参数
; 水印 “硬度” 参数
对于 执行以下操作:
- 对前序标记
应用语言模型,得到词汇表上的对数几率(logits)向量
。
- 计算标记
的哈希值,并用其作为随机数生成器的种子。
- 利用该随机数生成器,将词汇表随机划分为规模为
的 “绿名单” G 和规模为
的 “红名单” R。
- 给绿名单中每个词的对数几率加
。对这些修改后的对数几率应用 softmax 算子,得到词汇表上的概率分布:
- 利用带水印的分布
采样下一个标记
。
结束循环
算法2解读:
该算法旨在实现 “柔性水印嵌入”,核心是通过 “奖励绿名单而非禁止红名单” 的方式,平衡水印的可检测性与文本生成的自然性:
- 步骤 1:语言模型对前序标记计算 “下一个词候选” 的原始分数(logits 向量),这是模型预测下一词概率的基础。
- 步骤 2 - 3:借助前一个标记的哈希值生成随机种子,动态划分红、绿名单,既让水印具备 “隐式” 特点(无固定规律),又保证后续检测时能复现名单。
- 步骤 4:给绿名单词的 logits 加上
后再进行 softmax 转换,使绿名单词在采样时的概率得到提升(“奖励” 机制)。
- 步骤 5:基于修改后的概率分布采样下一个词,循环往复生成文本,每一步都悄然嵌入水印特征。
与 “硬红名单(直接禁止红名单词生成)” 相比,软红名单对低熵文本(如 “Barack” 之后几乎必然接 “Obama” 这类续写高度确定的情况)干预较弱(因为目标词的 logits 本身就很大,加对其生成概率影响极小),保障了文本流畅性;而对高熵文本(多个候选词生成概率相近的情况)干预较强,能隐性地将水印嵌入其中,是一种更为精巧的水印方案。
读到这里,你可能对于算法2的软水印对于低熵文本和高熵文本的处理还是模棱两可,我将分别举例这两种情况,希望对你有所帮助。
高熵文本的水印处理:
场景设定
要生成文本:“今天天气 ______ ,适合 ______ 。”
- 前序标记
是 “天气”;
- 参数:
(绿名单占词汇表一半),
(给绿名单词的 logits 加 2 分)。
步骤 1:生成基础 logits 向量
语言模型看前序标记 “天气”,给词汇表(假设候选词:“好”“坏”“晴朗”“下雨”“出游”“宅家”…)生成 logits 向量。 假设模型输出的 logits 为:
步骤 2:生成随机种子
计算前一个标记 =“天气” 的哈希值,假设哈希后得到种子 “12345”,用它启动随机数生成器。
步骤 3:划分红 / 绿名单
用种子 “12345”,把词汇表随机分成:
- 绿名单 G(规模
):假设包含 “好”“晴朗”“出游”;
- 红名单 R(规模
):假设包含 “坏”“下雨”“宅家”。
步骤 4:修改 logits 并生成水印概率分布
给绿名单 G 里的词加 \(\delta = 2\),修改后 logits:
- “好”:3 + 2 = 5
- “晴朗”:2 + 2 = 4
- “出游”:4 + 2 = 6
红名单 R 里的词 logits 不变:
- “坏”:1
- “下雨”:0
- “宅家”:-1
然后用 softmax 算概率分布 : 分母总和
。
其中
绿名单词的概率:
“好”:148.41 / 610.53 ≈ 0.243
“晴朗”:54.60 / 610.53 ≈ 0.089
“出游”:403.43 / 610.53 ≈ 0.661
红名单词的概率:
“坏”:2.72 / 610.53 ≈ 0.004
“下雨”:1 / 610.53 ≈ 0.002
“宅家”:0.37 / 610.53 ≈ 0.001
步骤 5:采样生成下一个词
根据采样,“出游” 的概率最高(约 0.661),所以生成
=“出游”。
此时文本变为:“今天天气 出游 ,适合 ______ 。” 接着重复流程生成下一个词,以此类推完成整个文本。
核心效果
- 绿名单词(“好”“晴朗”“出游”)因加了
,概率远高于红名单词;
- 但红名单词仍有极小概率被选(不是完全禁止),保障文本自然性;
- 高熵场景(“天气” 后可选词多)下,水印通过 “提升绿名单概率” 隐性嵌入
低熵文本的水印处理:
场景设定
要生成文本:“史学家司马 ______ 著有《史记》。”
- 前序标记
=“司马”(低熵场景核心:“司马” 后几乎只能接 “迁”);
- 参数:
(绿名单占词汇表一半),
(给绿名单词的 logits 加 2 分)。
步骤 1:生成基础 logits 向量
语言模型看到 “司马”,知道下一个词高度确定,给词汇表(候选词:“迁”“光”“相如”“懿”“炎”…)生成的 logits 会极度偏向 “迁”,假设输出:
步骤 2:生成随机种子
计算前一个标记 “司马” 的哈希值,假设得到种子 “54321”,用它启动随机数生成器。
步骤 3:划分红 / 绿名单
用种子 “54321” 随机划分词汇表,极端情况(故意让低熵核心词进红名单):
- 绿名单 G(占一半):包含 “光”“相如”“懿”;
- 红名单 R(占一半):包含 “迁”“炎”(“迁” 被分到红名单,本应被硬红名单禁止)。
步骤 4:修改 logits 并生成水印概率分布
给绿名单词加 分,修改后 logits:
- 绿名单:“光”-2+2=0、“相如”-3+2=-1、“懿”-4+2=-2;
- 红名单:“迁”10,不变、“炎”-5,不变。
用 softmax 算概率分布(核心看 “迁” 的概率): 各词指数值:
各词概率:
步骤 5:采样生成下一个词
根据概率分布,几乎 100% 会选中 “迁”,文本变为:“史学家司马迁著有《史记》。” 完全符合自然语言逻辑。
低熵场景的核心结论
软红名单在低熵场景的优势:
- 即使核心词(“迁”)被分到红名单,因它本身 logits 极高(强确定性),绿名单的加分根本无法撼动其主导地位 ——不会像硬红名单那样 “禁止核心词导致文本断裂”;
- 水印可通过后续高熵场景(比如 “《史记》是中国第一部 ______ 体史书”,可选 “纪传”“编年” 等)嵌入,不影响检测;
- 最终文本既自然合理,又保留水印嵌入可能,平衡了 “检测需求” 与 “文本质量”。
3.1 软水印的检测
软水印的检测流程与硬水印完全一致:遵循 “假设检验 + z 统计量判断” 的逻辑。
1.检测核心步骤
原假设与统计量: 先假设 “文本无水印(原假设
)”,再通过公式计算 z 统计量。若 z 统计量超过设定阈值,就拒绝原假设,判定文本含水印。 针对任意绿名单比例
,z 统计量公式为:
其中,
是文本中绿名单标记的数量,T 是文本总标记数,
是绿名单占词汇表的比例。
阈值与假阳性: 若选阈值
“误判自然文本为水印文本” 的假阳性率约为
(概率极低,检测很可靠)。
2.软水印 vs 硬水印:检测能力的差异
- 硬水印:只要文本长度 ≥ 16 个标记,无论文本本身特性如何(不管是低熵还是高熵内容),都能被检测到。
- 软水印:检测能力依赖文本的熵值(即文本续写的不确定性):
- 高熵序列(续写选项多、不确定性强):仅需少量标记就能检测到水印;
- 低熵序列(续写高度确定,如固定搭配):需要更多标记才能检测到。
3.后续分析预告
后文会严谨分析软水印的 “检测灵敏度”(即多容易检测到水印),以及这种灵敏度如何依赖于文本的熵值,进一步揭示软水印 “因熵制宜” 的检测特性。