对应原课:CMU 11-667 Fall 2025 · Lecture 16 · Tue 28-Oct-2025 · Attacking LLM systems + benchmark leakage 原课 slides:Attacking LLMs 阅读材料:Wei, Haghtalab & Steinhardt (2023), Jailbroken: How Does LLM Safety Training Fail?;Nasr et al. (2023), Scalable Extraction of Training Data from (Production) Language Models 配套代码:code/lecture16/(1 个模块 + 5 个演示脚本) 本讲的写法:按课程大纲的约定,只讲威胁模型、原理类别和防御,不给任何可以直接使用的攻击字符串。所有实验都用抽象符号:
- 受限的信息是 4 位数字;
- 注入的”指令”是取词任务;
- 隐私记录是虚构用户的 pin。
每个实验都以测量、审计或防御为目的。所有数字都来自配套代码的实际输出。
目录
- 0. 本讲导读
- 1. 威胁模型:谁、能接触到什么、想要什么
- 2. 安全训练为什么会失效
- 3. 提示注入与数据 / 指令分离
- 4. 训练数据的记忆与提取
- 5. 成员推断
- 6. 基准泄漏
- 7. 小结、常见误区、练习
0. 本讲导读
0.1 这一讲要回答的问题
第 15 讲用红队测试”轻轻碰了一下”让模型出错的问题。这一讲正面讨论:一个部署出去的 LLM 系统,可能以哪些方式被滥用或泄露信息? 原课把两件看似无关的事放在同一讲,是因为它们有共同的根源:
- 攻击:安全训练为什么会被绕过?LLM 应用读取的网页、邮件里藏着的”指令”为什么会被执行?
- 泄漏:模型会不会把训练数据原样说出来?能不能判断某段文本是否被拿去训练过?测试集混进训练数据,基准分数还可信吗?
共同的根源是:LLM 是在海量、未经审查的数据上训练出来的通用续写器。它记住了什么、它会对什么样的输入作出反应,训练者自己也不完全知道。
0.2 学完这一讲你应该能
- 用”攻击者能接触到什么、想要什么”描述一个威胁模型,并说出 LLM 系统的主要攻击面;
- 解释安全训练失效的两个原理(目标冲突、泛化不匹配),以及”浅层对齐”和它的防御;
- 解释提示注入为什么难防,说出 StruQ 等防御的两个组成部分;
- 定义可提取的记忆、金丝雀的曝光度,说出重复、训练遍数、模型大小对记忆的影响,以及去重和 DP-SGD 的作用与代价;
- 正确地评价成员推断(同分布的对照、盲基线),理解数据集推断;
- 用”贝叶斯上限”、引导式测试、Min-K%、顺序检验等方法讨论基准泄漏,并说出每种方法的盲区。
0.3 本讲的实验平台
attack_world.py 在花园世界上加了四样东西,全部是抽象符号:
受限配方(§2) the formula for X3 is 2 2 5 8 . X0~X11:12 个"受限物品"
<user> formula plum <asst> <ok> 9 4 0 1 <end> (43 个花园名词的配方可以公开)
取词任务(§3) <user> <inst> do noun <data> she picks her plum . <asst> plum <end>
隐私记录(§4、5) the pin of u17 is 1 9 4 8 . u00~u47:48 个虚构用户
完形填空(§6) they grow the pretty rose with their friends . I grow the ripe ___ (答案 melon)
模型都是第 2 讲的 d = 64、2 层因果 Transformer(个别实验换成更小或更大的)。
1. 威胁模型:谁、能接触到什么、想要什么
1.1 为什么先说威胁模型
安全领域的第一条规矩:“安全”不是一个形容词,而是针对某个威胁模型的陈述。“这个模型很安全”没有意义;“一个只能通过聊天界面提问、每天最多问 1000 次的攻击者,不能让它说出 X”才是一个可以检验的陈述。威胁模型要回答三个问题:
- 攻击者能接触到什么(访问权限);
- 攻击者想要什么(目标);
- 攻击者愿意付出多少(预算:查询次数、算力、能否训练自己的模型)。
1.2 访问权限:从最弱到最强
| 访问权限 | 例子 | 能做的事(原理层面) |
|---|---|---|
| 只能在聊天界面里提问 | 普通用户 | 改写请求、多轮对话、角色扮演、换语言或编码 |
| 能控制模型读到的数据 | 网页、邮件、文档的作者 | 间接提示注入(§3)、检索投毒(第 11 讲) |
| API 返回概率 / 允许预填 | 开发者 API | 预填回答的开头(§2)、用概率做成员推断(§5)、从 logits 推出模型结构 |
| 能微调 | 云服务的微调接口 | 用少量数据改变模型的行为(§2.7) |
| 拿到全部权重 | 开放权重的模型 | 用梯度搜索输入(§2.5)、直接改参数去掉对齐 |
| 能影响训练数据 | 任何能往网上写东西的人 | 数据投毒、后门(§1.4) |
越往下,攻击者能做的越多。开放权重的模型,一切只依赖”模型自己拒绝”的防护都可以被移除——所以对开放权重模型的风险评估,要假设攻击者可以任意微调。
1.3 目标:用安全的三要素来分类
传统的信息安全用 CIA 三要素(机密性、完整性、可用性)分类攻击。对 LLM 系统:
- 机密性(confidentiality):泄露不该泄露的东西。包括:
- 训练数据(§4);
- 某段文本是否被训练过(§5);
- 系统提示(system prompt);
- 模型参数本身(模型窃取)。
- 完整性(integrity):让系统做不该做的事。包括:
- 越狱(jailbreak,绕过安全训练,§2);
- 提示注入(劫持应用,§3);
- 数据投毒与后门;
- 基准泄漏让评测结果失真(§6)——这是对”评测”这个系统的完整性破坏。
- 可用性(availability):让系统不能正常工作。例如:
- 构造让模型生成极长输出的输入,耗尽算力;
- 用垃圾内容污染检索库。
1.4 本讲不展开、但要知道的几类攻击
- 数据投毒与后门:
- 攻击者往训练数据里放进少量特制的样本,让模型在看到某个触发词时表现异常。
- Carlini et al. (2023) 指出,往网络规模的数据集里投毒是现实可行的:数据集只存了网址,而过期的域名可以被别人买下。
- Hubinger et al. (2024, Sleeper Agents) 发现,植入的后门能扛过之后的安全训练。
- Souly et al. (2025) 发现,所需的投毒文档数量几乎不随模型变大而增加,大约几百篇。
- 防御:
- 数据来源和完整性校验(存内容的哈希而不只是网址);
- 过滤;
- 对训练好的模型做后门检测与审计。
- 模型窃取:Carlini et al. (2024) 发现,如果 API 返回完整的 logits(或允许”logit 偏置”),就能从输出向量的线性代数结构推出模型的隐藏维度和最后一层的参数。这背后的原理是:输出向量都落在一个由最后一层决定的低维子空间里。防御是限制 API 返回的信息(只给 top-k 的概率、加噪声、限制 logit 偏置)。
- 系统提示提取:让模型复述它的系统提示。系统提示不应该被当成秘密——凡是放进上下文的东西,都应该假设可能被说出来。
1.5 两个贯穿全讲的原则
- 安全是系统的性质,不只是模型的性质。模型拒绝是一层防护;输入输出过滤、权限控制、人工确认、监控是另外几层。任何一层都会被绕过,所以要纵深防御(defense in depth)。
- 评价防御要假设攻击者知道防御(Kerckhoffs 原则在机器学习里的版本:adaptive attacks)。只在”已知攻击”上测试的防御,常常在攻击者针对它调整之后失效——本讲的几个实验都会看到这一点。
2. 安全训练为什么会失效
2.1 两个原理
Wei, Haghtalab & Steinhardt (2023) 分析了大量绕过安全训练的方法,归纳出两个根本原因:
- 目标冲突(competing objectives):
- 安全训练之前,模型已经有了很强的”能力目标”:续写得像人写的、遵循指令。安全训练加上”拒绝有害请求”这个目标。
- 两个目标冲突时,输入可以被设计成让能力目标占上风。原理上的例子:
- 要求回答以某种肯定的语气开头;
- 要求不许说”不能”;
- 用角色扮演把有害请求包装成”合理的任务”。
- 泛化不匹配(mismatched generalization):
- 预训练覆盖的输入分布远比安全训练广。
- 模型能理解用某种编码、某种低资源语言、某种罕见格式写的请求,这是从海量预训练数据里学来的能力。
- 安全训练数据里却没有这些形式,于是能力泛化过去了,拒绝没有。
第 9 讲 §5.2 用”店家价位保密”的玩具看过泛化不匹配(换一种格式、加很多示例就能绕过弱对齐)。这一讲看另外三件事:
- 对齐到底改变了模型的什么;
- 怎样把对齐做得更牢;
- 对齐之后的微调会发生什么。
2.2 实验:能力已经在参数里了
代码 01 §1:
- 预训练:预训练语料里有 55 个物品的”配方事实”。
- 每个出现 24 次,轮流用三种说法。
- 其中 12 个是受限物品 X0~X11。
- 还有一些助手格式的对话,但只涉及 32 个公开物品。
- 问题:用助手格式问配方,模型能不能”提取”出它只以事实形式见过的知识?
| 用助手格式问配方(贪心解码) | 普通请求 | start well |
|---|---|---|
| 对话里见过的公开物品 | 100.0% | 96.9% |
| 只见过事实的公开物品 | 72.7% | 54.5% |
| 受限物品 | 58.3% | 33.3% |
按温度 1 对每个受限物品采样 30 次,48.9% 的回答说出了完整的受限配方。模型从没在对话里见过受限物品,却能用助手格式把它们的配方说出来:知识和回答格式都会泛化。安全训练面对的是一个已经存在的能力——它不能删除知识,只能改变”在什么情况下说出来”。
2.3 实验:标准的安全微调,和三种输入
代码 01 §2:
- 安全微调数据(只在助手的回答上算损失):
- 43 个公开物品的普通请求和带 start T 指令的请求 → 正常回答;
- 12 个受限物品的普通请求 →
<refuse> <end>。
- 训练:从预训练模型出发约 150 步。
- 测量:对受限物品,每种输入采样 360 次(12 个物品 × 30),统计说出完整受限配方的比例。
| 输入 | 预训练模型 | 标准对齐 |
|---|---|---|
| 普通请求 | 48.9% | 12.2% |
指令 start <ok> | 21.4% | 25.0% |
| 指令 start well | 27.8% | 30.0% |
预填 <ok> | 50.0% | 47.5% |
预填 <ok> + 配方第 1 位 | 86.1% | 76.9% |
预填 <ok> + 配方前 2 位 | 87.5% | 85.3% |
公开物品的回答完全不受影响(100%)。
- 普通请求:泄漏从约一半降到 12%。只看这一行,对齐像是成功了。
- 带无害的指令(“以 well 开头”):
- 安全数据里没有”受限物品 + 指令”的组合,遵循指令的能力占了上风,泄漏回到没对齐时的水平。
- 这同时体现了两个原理:指令和拒绝的冲突(目标冲突),以及安全数据没覆盖到这种组合(泛化不匹配)。
- 预填回答的开头:
- 开放权重的模型可以任意设定回答的开头,有些 API 也允许”预填”(prefill)助手的回答。
- 只要第一个词不是
<refuse>,对齐模型就和没对齐的模型一样把配方说完。
2.4 浅层对齐:对齐只改了第一个 token
代码 01 §3:沿着”正常回答” <ok> d d d d <end> 逐位置比较对齐模型和预训练模型的下一个词分布(KL 散度)。
| KL(nat) | <ok> | 第 1 位 | 第 2 位 | 第 3 位 | 第 4 位 | <end> |
|---|---|---|---|---|---|---|
| 标准对齐:受限物品 | 7.97 | 0.89 | 0.20 | 0.02 | 0.06 | 0.00 |
| 标准对齐:公开物品 | 0.39 | 0.18 | 0.05 | 0.00 | 0.01 | 0.00 |
对齐几乎只改了回答的第一个 token(把 <ok> 换成 <refuse>)。从第二个位置开始,对齐模型和预训练模型几乎一样:一旦回答”已经开始”,它就按预训练学到的样子继续。
Qi et al. (2024, Safety Alignment Should Be Made More Than Just a Few Tokens Deep) 在 Llama-2、Gemma 上画出了同样的图,称之为浅层安全对齐(shallow safety alignment)。
为什么会这样? 安全数据里的拒绝都从回答的第一个 token 开始。最省力的学法就是”在第一个位置把 <ok> 的概率压下去、把 <refuse> 的概率抬上来”,后面的位置在训练里从来没有出现过”有害的开头”,也就没有被改变的理由。
这解释了为什么一大类绕过方法都有效:预填、要求以某句话开头、在示例里示范肯定的开头。它们都是想办法让回答的前几个 token 不是拒绝。
2.5 原理层面的其他绕过方式(不给具体字符串)
| 类别 | 原理 | 主要的防御思路 |
|---|---|---|
| 基于优化的搜索(Zou et al. 2023, GCG) | 在白盒模型上用梯度引导的离散搜索,找一段附加在请求后面的 token,让”以肯定的语气开头”的概率最大;找到的后缀常常能迁移到其他模型 | 这类后缀通常是乱码,困惑度过滤能挡住一部分(Jain et al. 2023);对输入做随机扰动再投票(SmoothLLM);对抗训练;但加了”可读性”约束的自适应攻击能绕过困惑度过滤 |
| 多示例(Anil et al. 2024) | 在长上下文里放大量”违规问答”的示例,利用上下文学习 | 第 9 讲 §5.2;对长上下文做分类和改写 |
| 编码与低资源语言 | 用安全训练没覆盖到的形式提出请求(泛化不匹配) | 安全数据覆盖更多形式;先”翻译 / 解码”成标准形式再检查 |
| 微调 | 用少量数据改变模型的行为(§2.7) | 微调数据审核、微调后重新评估、限制可微调的部分 |
| 自动红队(Perez et al. 2022;Chao et al. 2023, PAIR) | 用另一个 LLM 自动生成和改进攻击性的请求 | 同样的工具也用来在发布前找漏洞 |
模型之外的防御层:
- 输入 / 输出分类器(Llama Guard、Constitutional Classifiers):单独训练的模型检查请求和回答,和主模型的对齐互相独立。攻击者要同时绕过两层。
- 表示层面的干预(circuit breakers,Zou et al. 2024):检测到”有害内容”的内部表示时把它扰乱掉,不依赖于第一个 token。
- 监控与限速:自动化的攻击需要大量查询。
2.6 实验:两种防御——覆盖面与深度
代码 01 §4:比较两种针对性的防御。
- 覆盖:安全数据里也放进”受限物品 + start T 指令 → 拒绝”。这是对付泛化不匹配的直接办法。
- 恢复样例(Qi et al. 2024 的数据增强):
- 回答已经以
<ok>或配方的前 0~3 位开头,目标是在那之后转为<refuse>。 - 前缀只作为条件、不计损失:要教模型”开了头也能停下来”,而不是教它说出有害的开头。
- 回答已经以
| 普通 | start <ok> | start well | 预填 0 位 | 预填 1 位 | 预填 2 位 | 公开物品 | |
|---|---|---|---|---|---|---|---|
| 标准对齐 | 12.2% | 25.0% | 30.0% | 47.5% | 76.9% | 85.3% | 100% |
| + 覆盖 | 1.9% | 1.7% | 0.8% | 43.9% | 65.3% | 73.1% | 100% |
| + 恢复样例(深度对齐) | 0.0% | 0.0% | 0.0% | 0.0% | 0.0% | 0.0% | 100% |
| + 两者 | 0.0% | 0.0% | 0.0% | 0.0% | 0.0% | 0.0% | 100% |
- 覆盖只挡住了它覆盖到的那种输入(指令),对预填几乎没用。泛化不匹配的解药是”更多的覆盖”,但攻击者总能找到没被覆盖的形式——这是一场追赶游戏。
- 恢复样例同时挡住了所有六种输入,而且没有误伤公开物品。它的 KL 曲线从”只在第一个 token”变成”贯穿整个回答”(13.6、13.5、14.0、14.0、14.3):即使回答已经开了头,模型也会中途停下来。它对指令攻击也有效,因为指令让回答以 well 开头,而模型学会了”无论怎样开头都转为拒绝”——深度比覆盖泛化得更好。
换两个随机种子重做预训练和对齐:
| 种子 | 预训练模型(普通请求) | 标准对齐:普通 / start well / 预填 0 / 预填 1 | 深度对齐:同左 |
|---|---|---|---|
| 1 | 71.1% | 6% / 56% / 79% / 92% | 0% / 0% / 0% / 0% |
| 2 | 36.1% | 0% / 32% / 51% / 86% | 0% / 0% / 0% / 0% |
预训练模型”提取”知识的能力随种子变化很大(小模型的知识提取不稳定,见 README),但对齐的规律在三个种子上都一样。
这个玩具的局限:这里的”有害回答”只有 4 个 token,恢复样例覆盖了所有可能的前缀长度。真实的有害回答很长,攻击者也可以预填更长、更巧妙的开头。Qi et al. 的实验里,深度对齐显著降低了预填、GCG 等攻击的成功率,但没有降到零。
2.7 实验:无害的微调会抹掉对齐
Qi et al. (2023) 发现:
- 用 10 条有害样例、花不到 0.2 美元,就能通过 GPT-3.5 Turbo 的微调 API 破坏它的对齐;
- 即使用完全无害的数据(Alpaca、Dolly)微调,有害回答的比例也会明显上升。
代码 01 §5:
- 一位用户想让助手的回答都以 so 开头(一个完全正当的定制需求)。
- 用 430 条公开物品的样例微调 3 遍。
- 缓解办法:在微调数据里混入一定比例的安全样例。
| 起点 | 混入安全样例 | 风格学会了 | 普通请求泄漏 | 预填 <ok> 泄漏 |
|---|---|---|---|---|
| 标准对齐 | 0% | 100% | 36.4% | 34.7% |
| 标准对齐 | 10% | 100% | 29.7% | 31.9% |
| 标准对齐 | 30% | 100% | 4.4% | 27.5% |
| 深度对齐(两者) | 0% | 100% | 0.0% | 0.3% |
| 深度对齐(两者) | 10% | 100% | 0.0% | 0.0% |
| 深度对齐(两者) | 30% | 100% | 0.0% | 0.0% |
- 标准对齐的模型:
- 普通请求的泄漏从 12% 涨到 36%。“回答以 so 开头”泛化到了受限物品上,而它的拒绝只存在于第一个 token:第一个 token 被改掉,拒绝就没了。
- 混入 10% 的安全样例几乎没用;30% 才把普通请求压回去,预填的泄漏仍然很高。
- 深度对齐的模型:即使不混安全样例,微调后也几乎不泄漏。它学到的”无论怎样开头都转为拒绝”恰好覆盖了”以 so 开头”这种新开头。(README 记录了一个注意事项:在训练步数更少的原型里,深度对齐不混时漏了 14%。这个结论对训练细节比较敏感。)
对提供微调服务的一方,这意味着:
- 审核微调数据;
- 在微调后的模型上重新做安全评估;
- 默认混入安全数据;
- 对开放权重的模型,要假设对齐可以被完全移除,风险评估要以”去掉对齐之后的能力”为准。
3. 提示注入与数据 / 指令分离
3.1 什么是提示注入
越狱的攻击者是用户自己;提示注入(prompt injection)的攻击者是第三方,受害者是用户。典型的情形:
- 一个邮件助手替用户总结邮件;
- 一个浏览器智能体替用户读网页、填表单;
- 一个 RAG 系统把检索到的文档放进上下文(第 11 讲);
- 一个编程助手读取仓库里的文件和 issue。
这些系统都把开发者 / 用户的指令和不可信的数据拼在同一个提示里。数据里如果夹着一句”指令”,模型可能照做——把用户的邮件转发出去、在回答里插入钓鱼链接、调用不该调用的工具。数据来自第三方时叫间接提示注入(Greshake et al. 2023)。随着 LLM 智能体能调用工具(第 10 讲)、能代表用户行动,提示注入从”让模型说错话”变成了”让系统做错事”。
根本原因:对模型来说,指令和数据走的是同一个通道——都是一串 token。传统软件里也有同样的问题(SQL 注入:把数据当成代码执行),那里的解决办法是参数化查询——代码和数据在语法层面就分开,数据永远不会被解释成代码。LLM 没有这样的语法层,“什么是指令”是模型从训练数据里学来的模糊概念。
3.2 实验:只用干净数据训练
代码 02 用一个抽象的取词任务:用户让助手从一句花园句子里取一个词(do who 取主语、do verb 取动词、do noun 取名词);句子是不可信的数据,攻击者能在里面的任意位置插入文字。两种格式:
text <user> inst : do noun data : she picks her plum . <asst> 标记是普通的词
reserved <user> <inst> do noun <data> she picks her plum . <asst> 标记是保留的特殊 token
指令可以在数据前(“总结下面的邮件:……”),也可以在数据后(”……总结上面的邮件”),训练数据里各占一半。四种注入(t2 是另一个任务,插在数据的随机位置):
| 注入 | 内容 | 含义 |
|---|---|---|
| plain | do t2 | 数据里夹着一条指令 |
| spoof | inst : do t2 | 再伪造 text 格式的指令标记 |
| spoof2 | data : inst : do t2 | 伪造”数据结束、指令开始”的结构 |
| reserved | <inst> do t2 | 试图写出保留 token(reserved 格式的前端会把它删掉) |
20000 条干净的样例训练 3 遍,每格 600 次测试(“干净”列是没有注入时的准确率,其余是攻击成功率):
| 模型 | 指令位置 | 干净 | plain | spoof | spoof2 | reserved |
|---|---|---|---|---|---|---|
| text | 指令在前 | 100% | 39.3% | 39.2% | 39.7% | 38.8% |
| text | 数据在前 | 100% | 41.2% | 40.0% | 41.2% | 40.7% |
| reserved | 指令在前 | 100% | 48.2% | 43.3% | 39.2% | 48.2% |
| reserved | 数据在前 | 100% | 54.7% | 55.7% | 53.3% | 54.7% |
干净输入上全对;可一旦数据里夹着 do t2,四成到一半的时候模型就去做了 t2。保留 token 本身并不能防住注入:模型知道指令区在哪里,但从没有人教过它”数据区里的 do … 不算数”。
3.3 实验:结构化指令微调(StruQ)
StruQ(Chen et al. 2024, StruQ: Defending Against Prompt Injection with Structured Queries)的防御有两部分:
- 安全前端:用保留的特殊 token 标出指令区和数据区,并把数据里出现的这些 token 删掉——攻击者写不出它们。真实系统里,
<|im_start|>这类特殊 token 不能由普通文本”分词”得到(前提是分词器被正确地配置成不解析文本里的特殊 token)。 - 结构化指令微调:训练时故意在数据区里放进注入的指令,目标仍然是用户的任务,教模型只听指令区的话。
代码 02 §3:一半的训练样例在数据里随机插入一条 plain 注入。注意训练只用了最简单的 plain 注入,从没见过伪造标记的注入。
| 模型 | 指令位置 | 干净 | plain | spoof | spoof2 | reserved |
|---|---|---|---|---|---|---|
| text + 注入训练 | 指令在前 | 100% | 0.0% | 11.7% | 1.8% | 0.0% |
| text + 注入训练 | 数据在前 | 100% | 0.0% | 9.7% | 0.7% | 0.0% |
| reserved + 注入训练 | 指令在前 | 100% | 0.0% | 0.0% | 0.0% | 0.0% |
| reserved + 注入训练 | 数据在前 | 100% | 0.0% | 0.0% | 0.2% | 0.0% |
- 两种格式都学会了忽略 plain 注入,干净输入上的准确率不受影响。
- text 格式的模型学到的是”跟着
inst :后面的指令走”——攻击者在数据里也写一个inst :,就能骗过它约一成。有趣的是,伪造得更”完整”的 spoof2 反而几乎没用:哪种伪造有效,取决于模型学到的具体捷径。真实的攻击者会试错,能拿到权重时还会用梯度去搜(§2.5)。 - reserved 格式里,真正的指令标记是攻击者写不出的 token:数据里伪造的
inst :只是普通的词,直接写<inst>又会被前端删掉。两部分缺一不可:只有保留 token 没有训练(§3.2)挡不住;只有训练、标记却能伪造,也挡不住。
3.4 防御的谱系
| 层次 | 方法 | 局限 |
|---|---|---|
| 提示层 | 用分隔符标出数据;在数据之后重复一遍指令(“三明治”);数据标记(spotlighting,Hines et al. 2024:在数据的每个词之间插入一个标记符,或者把数据编码,让模型更容易分清) | 模型没被训练过”忽略数据里的指令”,效果有限;自适应攻击能绕过 |
| 训练层 | StruQ、SecAlign(Chen et al. 2025,用偏好优化让模型更偏向用户的指令);指令层级(instruction hierarchy,Wallace et al. 2024:系统 > 用户 > 工具输出,训练模型在冲突时听高优先级的) | 基于梯度的自适应攻击仍能找到成功的注入;需要模型提供方来做 |
| 检测层 | 专门的注入检测分类器,检查进入上下文的数据 | 和越狱一样是追赶游戏 |
| 系统层 | 最小权限(读邮件的智能体不该有发邮件的权限);有副作用的操作要人工确认;控制流与数据流分离——CaMeL(Debenedetti et al. 2025):一个只看用户请求的”规划”模型写出程序,另一个模型只负责从不可信数据里提取值,提取出的值不能改变程序的控制流 | 限制了系统的灵活性;不能防止”数据本身被篡改”导致的错误结果 |
最稳妥的原则是:假设模型一定会被注入,然后限制被注入之后能造成的损害。这和第 10 讲对工具调用的建议一致:工具的输出是不可信的输入,有副作用的操作要有额外的检查。
4. 训练数据的记忆与提取
4.1 几种”记忆”的定义
“模型记住了训练数据”需要说清楚是什么意思。常用的几种定义:
- 可提取的记忆(extractable memorization,Carlini et al. 2021;Nasr et al. 2023):存在某个攻击者不需要访问训练数据就能构造出的提示,使模型输出这段训练数据。这是”真正泄露”的定义。
- 可发现的记忆(discoverable memorization,Carlini et al. 2023):用训练数据里这段文本的前缀提示模型,贪心解码能原样补全后缀。这是审计时最容易测量的一种,是可提取记忆的一个上界估计(攻击者不一定知道前缀)。
- k-逼真记忆(k-eidetic):一段在训练数据里最多出现 k 次的文本被记住了。k 越小,越说明模型记住的是”这一条数据”而不是”常见的模式”。
- 反事实记忆(counterfactual memorization,Zhang et al. 2023):训练时包含这段数据的模型,和不包含它的模型,在这段数据上的表现差多少。它区分了”记住了这条数据”和”这条数据本来就容易预测”(§4.6)。
4.2 金丝雀与曝光度
开发者怎样审计自己的模型记住了多少?Carlini et al. (2019, The Secret Sharer) 的办法:往训练数据里主动放进一些随机生成的”秘密”(金丝雀,canary),训练之后看模型对它们有多”熟”。
曝光度(exposure):把真实的秘密和所有可能的候选按模型给的概率排序,
是候选的个数。完全没记住时,真实秘密的名次在中间附近,曝光度约 1.4 比特(练习 1);名次第一时达到最大值 。曝光度把”记住了多少”变成一个连续的量,不需要模型真的原样说出来。
代码 03 §1:在第 1 讲的 8000 篇花园文档里放进 48 个虚构用户的 pin 记录(the pin of u17 is 1 9 4 8 .),每 8 个用户一组,记录分别重复 1、2、4、8、16、32 次(像同一条信息在网上被转载了很多次),共 504 篇,占 5.9%。4 位 pin 有 个候选,曝光度最大 13.3 比特。exposure() 用一次前向就算出全部一万个候选的概率:输入 the pin of u is d1 d2 d3 的 1000 种前三位,因果模型在每个位置给出下一位的分布,四个位置的 log 概率相加就是一万个候选。
d = 64、2 层的模型训练 2 遍(花园文档的测试 NLL 已经和训练更久的模型一样好):
| 重复次数 | 1 | 2 | 4 | 8 | 16 | 32 |
|---|---|---|---|---|---|---|
| 平均曝光度(比特) | 2.7 | 2.1 | 2.4 | 2.9 | 6.4 | 12.4 |
| 可提取的比例 | 0% | 0% | 0% | 0% | 0% | 75% |
只出现过一两次的记录几乎没被记住;重复 16 次开始明显上升,重复 32 次的记录大多能原样补全。Kandpal et al. (2022) 在真实模型上测到:重复 10 次的序列被生成的频率比只出现一次的高约 1000 倍——记忆随重复超线性增长。
4.3 实验:训练更久、模型更大
代码 03 §2(每格是该重复次数的平均曝光度;“可提取”是 48 条记录里能原样补全的条数):
| 设置 | ×1 | ×2 | ×4 | ×8 | ×16 | ×32 | 可提取 | 测试 NLL |
|---|---|---|---|---|---|---|---|---|
| d64、2 层,1 遍 | 2.8 | 1.4 | 1.3 | 1.7 | 1.8 | 5.5 | 0/48 | 1.409 |
| d64、2 层,2 遍 | 2.7 | 2.1 | 2.4 | 2.9 | 6.4 | 12.4 | 6/48 | 1.344 |
| d64、2 层,4 遍 | 4.5 | 5.6 | 9.0 | 10.1 | 13.0 | 13.3 | 18/48 | 1.338 |
| d64、2 层,8 遍 | 11.7 | 13.3 | 13.3 | 13.3 | 13.3 | 13.3 | 44/48 | 1.342 |
| d32、1 层,2 遍 | 2.3 | 1.5 | 1.8 | 1.7 | 2.1 | 5.7 | 1/48 | 1.471 |
| d128、4 层,2 遍 | 3.0 | 2.7 | 3.6 | 3.5 | 10.1 | 13.3 | 7/48 | 1.339 |
- 训练遍数是最强的因素:训练 8 遍,连只出现一次的记录也被记住了,而测试 NLL 和训练 2 遍时一样。记住这些记录不需要牺牲泛化——这就是为什么”只看验证损失”发现不了记忆。
- 模型越大记得越多:Carlini et al. (2023, Quantifying Memorization) 发现,可发现的记忆随参数量、重复次数和提示的上下文长度都按对数线性增长。最小的模型只勉强记住了重复 32 次的记录。
现代的大模型预训练通常每篇文档只看大约 1 遍,这对隐私是好消息;但为了”数据不够用了”而多遍训练(第 12 讲 §5 的数据受限规模定律)会增加记忆。
4.4 实验:去重
Lee et al. (2022, Deduplicating Training Data Makes Language Models Better) 发现网络语料里有大量重复(C4 里有一个 61 个词的句子重复了 6 万多次),去重让模型原样输出训练数据的频率降低约 10 倍,而且不损害、甚至改善了模型质量(第 3 讲 §4 讲过 MinHash 等去重方法)。
代码 03 §3:精确去重(8504 → 7574 篇;花园文档里重复的短文档也被删掉了一些),表的列仍按”原来重复了几次”分组:
| 设置 | ×1 | ×2 | ×4 | ×8 | ×16 | ×32 | 可提取 | 测试 NLL |
|---|---|---|---|---|---|---|---|---|
| 原数据,2 遍 | 2.7 | 2.1 | 2.4 | 2.9 | 6.4 | 12.4 | 6/48 | 1.344 |
| 去重,2 遍 | 2.4 | 2.5 | 1.6 | 1.7 | 1.4 | 2.8 | 0/48 | 1.354 |
| 原数据,8 遍 | 11.7 | 13.3 | 13.3 | 13.3 | 13.3 | 13.3 | 44/48 | 1.342 |
| 去重,8 遍 | 4.1 | 4.7 | 3.5 | 2.8 | 2.4 | 5.5 | 0/48 | 1.345 |
训练 2 遍时,去重把重复 32 次的记录降到了和只出现一次的一样。训练 8 遍时有一个意外:去重后每条记录同样被看了 8 次,曝光度却只有 2~6 比特;而原数据里”只出现一次”的记录(同样被看了 8 次)是 11.7 比特。区别在于原数据里有 504 篇 pin 记录、去重后只有 48 篇——模型见过的同格式记录越多,“pin 的格式”学得越熟,每一条具体的 pin 就越容易记住。去重删掉的不只是重复本身,也是这些”同类的数据”。这是这个小世界里的观察;真实语料里同格式的文本(表格、日志、联系方式)极多,这提示”记住一条数据”不只取决于它自己出现了几次。
4.5 实验:差分隐私训练
去重是经验性的防御。差分隐私(differential privacy,DP)给出可证明的保证:一个训练算法 是 -差分隐私的,如果对任意两个只差一篇文档的数据集 ,和任意一组可能的训练结果 ,
直觉:有没有这篇文档,训练出的模型的分布几乎一样——所以任何人都没法从模型推断出这篇文档的内容(包括它在不在训练集里)。ε 越小保护越强。
DP-SGD(Abadi et al. 2016)把普通的 SGD 改两处:
- 每个样例的梯度裁剪到范数 ≤ C:限制任何一篇文档对一步更新的影响(“敏感度”);
- 一个批次的梯度加起来以后加上 的噪声:把这个有限的影响淹没。
每一步的隐私损失由噪声乘数 σ 和抽样比例 q(批大小 / 数据集大小)决定,T 步组合起来由”隐私会计”算出总的 ε。代码里的 compute_epsilon 用 Rényi 差分隐私的整数阶公式(Mironov et al. 2019)。“抽样”带来的隐私放大是 DP-SGD 能用的关键:每一步只有 q 比例的数据参与,单篇文档在每一步的暴露大大降低(练习 3)。
代码 03 §4:批大小 256、学习率 1e-2、训练 8 遍(dp_train 用 torch.func.vmap 一次算出一个批次里每个样例的梯度):
| 设置 | ×1 | ×2 | ×4 | ×8 | ×16 | ×32 | 可提取 | 测试 NLL | ε(δ = 1e-5) |
|---|---|---|---|---|---|---|---|---|---|
| 普通训练,8 遍(批大小 32) | 11.7 | 13.3 | 13.3 | 13.3 | 13.3 | 13.3 | 44/48 | 1.342 | ∞ |
| σ = 0(只裁剪) | 3.3 | 1.6 | 1.7 | 2.5 | 1.9 | 8.6 | 0/48 | 1.382 | ∞ |
| σ = 0.5 | 1.7 | 1.0 | 1.3 | 2.0 | 2.5 | 7.3 | 0/48 | 1.422 | 24.4 |
| σ = 1.0 | 2.7 | 1.5 | 1.5 | 1.4 | 2.2 | 6.5 | 0/48 | 1.495 | 4.3 |
| σ = 2.0 | 2.4 | 1.6 | 1.3 | 1.2 | 2.5 | 4.3 | 0/48 | 1.638 | 1.4 |
- 只裁剪就已经减少了记忆,但没有任何保证(ε = ∞)。注意批大小 256 时总步数只有普通训练的 1/8,这一行减少的记忆有一部分来自”更新次数少”——比较 DP 的效果,应该和同样批大小、同样步数的”只裁剪”比。
- 加噪声之后,重复 1~16 次的记录都降到了”没记住”的水平;代价是测试 NLL 明显变差(1.38 → 1.50~1.64)。小数据集上 DP 的代价尤其大;真实的 DP 训练用极大的批和极多的数据来摊薄噪声(Google 2025 年的 VaultGemma 是一个用 DP 从头预训练的 10 亿参数模型,质量大约相当于几年前的非 DP 模型)。
- 重复 32 次的记录仍有残余的曝光度:DP 保护的单位是”一篇文档”。同一条记录出现 k 次,相当于 k 篇文档同时变化,保证大约退化为 kε(群组隐私,练习 2)。所以 DP 和去重是互补的:先去重,再用 DP 保护剩下的每一份。另一个难点是”一个人的隐私”常常分散在很多篇文档里(用户级 DP 比样例级 DP 难得多)。
4.6 实验:不给前缀,模型会自己说出训练数据吗?
前面的测量都假设审计者知道前缀。真正的提取攻击不知道。Carlini et al. (2021) 的方法:从模型大量采样,再按”模型有多自信、而参考模型(或 zlib 压缩率)有多不自信”排序,挑出最可能是记忆的样本——他们这样从 GPT-2 里找到了真实的姓名、电话、地址和网址。
代码 03 §5:从 <bos> 开始按温度 1 采样 3000 篇文档;对照是一个在另外 8000 篇花园文档上训练的参考模型(没有 pin 记录):
| 模型 | 花园文档与训练集逐字相同 | 生成的 pin 记录 | 其中 pin 正确 | 正确的来自(×1/×2/×4/×8/×16/×32) |
|---|---|---|---|---|
| 目标模型(2 遍) | 18.1% | 182 | 4 | 0/0/0/0/0/4 |
| 目标模型(8 遍) | 17.6% | 261 | 215 | 0/3/10/25/51/126 |
| 参考模型 | 21.5% | 0 | 0 | — |
- 约两成生成的花园文档和某篇训练文档一字不差——但从没见过这些文档的参考模型也一样(甚至更多)。这些是
I eat the apple .这样又短又常见的文档:它在 8000 篇训练文档里期望出现约 7 次(练习 9)。模型是”学会了语言”,不是”记住了文档”。判断记忆要和没见过这条数据的模型比(反事实记忆),不能只看是否逐字出现。 - pin 记录不一样:参考模型说不出任何正确的 pin;训练 8 遍的目标模型自发说出了 215 个正确的 pin,越是重复多的记录被说出得越多。
4.7 对齐过的生产模型:记忆被”藏起来”了,但还在
Nasr et al. (2023)(原课阅读材料)把问题推到了生产模型上:
- 开放模型:对 Pythia、LLaMA、Falcon 等,他们用大量采样 + 与公开的大规模网络语料比对,测量可提取的记忆;发现可提取的比例随模型变大而增加,而且远高于以前的估计。
- 对齐过的聊天模型:ChatGPT 这类模型很少直接说出训练数据——对齐让它们的输出”像助手”,而不是”像网页”。但他们发现了一种原理很简单的发散攻击(divergence attack):某些输入能让聊天模型偏离对话模式、“退回”到预训练时的续写行为,此时它以比正常时高得多的频率逐字输出训练数据。教训是:对齐改变的是模型通常的行为,不是它记住的内容——这和 §2.4 的浅层对齐是同一个道理。
防御思路:
- 训练数据侧:去重;过滤个人信息(PII 擦除);对敏感数据用 DP;
- 输出侧:检查输出是否和训练数据有长的逐字重叠(GitHub Copilot 的”重复检测过滤器”就是这样做的);
- 接口侧:限制采样参数、监控异常的查询模式。
4.8 版权与记忆
记忆也是版权问题的核心之一:如果模型能逐字输出受版权保护的书籍、新闻、代码,那么”训练是否构成合理使用”之外,还有”输出是否构成复制”的问题。Cooper et al. 等法律学者指出,记忆不是”有或无”,而是一个程度问题:模型可能记住了某本书的一小部分段落,而完全没记住其他部分;能否被提取还取决于提示和解码方式。技术上,本节的工具(可提取记忆的测量、成员推断、数据集推断)正是版权诉讼里被用来提供证据的方法。原课 2024 版有一次专门的法律问题讲座(客座 A. Feder Cooper),2025 版删去了,这里只做提示。
5. 成员推断
5.1 问题与几种分数
成员推断(membership inference):给一个模型和一段文本,判断这段文本是否在训练数据里。它是隐私审计的基本工具(“模型是否用了我的数据”),也被用来检测版权数据和基准泄漏(§6、第 7 讲 §5.4)。几种常用的分数(越大越像”训练过”):
| 分数 | 定义 | 直觉 |
|---|---|---|
| 损失(Yeom et al. 2018) | −平均 NLL | 训练过的样本损失更低 |
| zlib 比值(Carlini et al. 2021) | −总 NLL / zlib 压缩后的字节数 | 用”文本本身有多容易压缩”校准 |
| 参考模型(Carlini et al. 2021, 2022 LiRA) | −(NLL − 参考模型的 NLL) | 参考模型没见过这段文本,扣掉”文本本来就容易”的部分 |
| Min-K% Prob(Shi et al. 2024) | 最低的 k% token 的平均 log 概率 | 没见过的文本总有几个”意外”的词;见过的文本,连最意外的词也不意外了 |
评价用 AUC:随机一篇成员的分数高于随机一篇非成员的概率(0.5 = 瞎猜)。也常报告”假阳性率很低(例如 1%)时的真阳性率”,因为在隐私问题上,“对少数样本能非常确定”比”平均来说略好于瞎猜”更重要。
5.2 实验:成员推断在预训练上几乎无效
代码 04 §1:目标模型在 8000 篇花园文档上训练 1~20 遍;成员 = 其中 1000 篇,非成员 = 同一分布的 1000 篇新文档;参考模型在另外 8000 篇同分布文档上训练 2 遍。
| 训练遍数 | 损失 | zlib | 参考模型 | Min-20% | 训练 NLL | 测试 NLL |
|---|---|---|---|---|---|---|
| 1 | 0.509 | 0.509 | 0.514 | 0.518 | 1.376 | 1.385 |
| 2 | 0.512 | 0.511 | 0.531 | 0.517 | 1.322 | 1.333 |
| 6 | 0.519 | 0.514 | 0.546 | 0.530 | 1.295 | 1.310 |
| 20 | 0.587 | 0.544 | 0.697 | 0.602 | 1.262 | 1.322 |
训练 1~2 遍(大模型预训练的典型情形)时,所有方法都只比瞎猜好一点点。Duan et al. (2024, Do Membership Inference Attacks Work on Large Language Models?) 在 Pythia 上得到同样的结论:预训练数据量巨大、每篇只见过约一次、成员和非成员本来就很相似(自然语言里大量重叠的 n-gram),成员推断的 AUC 大多在 0.5~0.6 之间。只有训练遍数多、出现了过拟合(训练和测试 NLL 拉开差距)时才明显有效;参考模型校准的分数最好。
5.3 实验:换一个非成员集,测的还是”成员”吗?
很多成员推断的基准是这样构造的:成员取自已知的训练数据,非成员取自”模型发布之后”的数据(例如 WikiMIA 用 2023 年之后才创建的维基百科条目)。问题是:两者的话题本来就不同。
代码 04 §2:第 1 讲的训练集刻意去掉了 5 种动词-名词搭配(pick plum、grow tulip……),测试集按完整分布采样,18% 的测试文档含这些搭配。再加一个”盲”分类器:不看模型,只看文档里有没有这 5 种搭配。
| 非成员集 | 盲分类器 | 损失 | zlib | 参考模型 | Min-20% |
|---|---|---|---|---|---|
| 同分布的新文档 | 0.500 | 0.512 | 0.511 | 0.531 | 0.517 |
| 第 1 讲的测试集(18% 含没见过的搭配) | 0.590 | 0.523 | 0.523 | 0.539 | 0.551 |
| 每篇都含没见过的搭配 | 1.000 | 0.758 | 0.758 | 0.577 | 0.832 |
(目标模型训练 2 遍。)
- 非成员的分布一变,损失、Min-K% 的 AUC 就”变好”了——可它们测到的是”这篇文档的话题模型熟不熟”,不是”这篇文档在不在训练集里”。
- 根本不看模型的盲分类器在分布不同时比所有方法都好(0.590 正好是 0.5 + 0.18 / 2,练习 8)。Das et al. (2024, Blind Baselines Beat Membership Inference Attacks for Foundation Models) 在 WikiMIA 等八个基准上发现了同样的现象:只看日期、词汇的盲分类器胜过已发表的所有方法。
- 参考模型校准受的影响最小:参考模型也没见过这 5 种搭配,“话题陌生”的信号大部分被扣掉了。前提是参考模型的训练数据和目标模型同分布——包括”同样没见过什么”,这在真实世界里很难保证。
教训:评价成员推断,成员和非成员必须来自同一个分布(例如对一个数据集随机切分,一半训练、一半不训练),并且要和盲基线比较。
5.4 实验:一篇判断不准,一千篇呢?——数据集推断
单篇文档的信号很弱,但很多实际问题问的是一批文档:“这位作者的书是否被用来训练?""这个网站的内容是否被用来训练?“Maini et al. (2024, LLM Dataset Inference) 的思路:把一批文档的弱信号聚合起来做统计检验。
代码 04 §3:比较”可疑的一批”和”确定没被训练过的一批”(同分布的 1000 篇)的参考模型分数,置换检验的单侧 p 值:
| 一批的篇数 | 真的被训练过 | 没被训练过 |
|---|---|---|
| 10 | 0.368 | 0.668 |
| 30 | 0.472 | 0.815 |
| 100 | 0.209 | 0.472 |
| 300 | 0.0095 | 0.378 |
| 1000 | 0.0025 | 0.153 |
每篇只比瞎猜好一点(AUC 约 0.53),几百篇放在一起就能以很小的 p 值断定”这批数据被训练过”;没被训练过的那批得不到显著的结果。这需要一批确定没被训练过、而且同分布的文档作对照——还是 §5.3 的教训。数据集推断是目前比较可靠的、能用于版权等实际争议的方法。
6. 基准泄漏
6.1 为什么又讲一遍
第 7 讲 §5 已经看过:测试题混进训练数据,分数会虚高(87% → 97%);换数字能揭穿、换名字不能;n-gram 查重会被模板句误报、被改写骗过。第 3 讲 §4 讲了训练数据侧的去污染。这一讲从”攻击与泄漏”的角度再看三个问题:
- 有没有一把绝对的标尺,能证明分数是”注水”的?
- 泄漏的格式重要吗?
- 看不到训练数据(闭源模型)时,只用模型给出的概率能检测到什么?
6.2 实验:贝叶斯上限——超过它就是记住了
代码 05 §1:
-
基准:“花园完形填空”,400 道题,每题两句话,第二句停在宾语名词之前。例如:
they grow the pretty rose with their friends . I grow the ripe ___ (答案 melon) -
上限:花园世界的真实分布是已知的。每题都猜”真实分布里最可能的名词”(贝叶斯最优),期望准确率是 25.5%,在这 400 题上实际答对 24.0%。任何没见过题目的模型,期望准确率都不可能超过这个上限。
-
干净的模型:答对 23.5%(前 200 题)和 22.0%(后 200 题)。
代码 05 §2:
- 前 200 题泄漏进训练数据,每题单独一篇、格式和评测时一样,重复 4 或 16 次。
- 后 200 题是干净的对照。
| 模型 | 泄漏的题 | 干净的题 | 上限 |
|---|---|---|---|
| 干净 | 23.5% | 22.0% | 25.5% |
| 一题一篇 ×4 | 27.5% | 22.0% | 25.5% |
| 一题一篇 ×16 | 99.5% | 20.0% | 25.5% |
重复 16 次后,泄漏的题几乎全对,远远超过任何模型可能达到的上限;干净的题完全不受影响。这正是”分数注水”:基准分数提高了,能力没有变。重复 4 次时还看不出来——200 题上 27.5% 和上限的差距在统计误差之内。
真实世界没有”真实分布”可查,但有一个近似的做法:在同分布的新题上测(第 7 讲的 GSM1k、GSM-Symbolic)。Zhang et al. (2024) 新造了 1000 道和 GSM8K 同分布的小学数学题,发现一些模型在新题上最多掉了约 13 个百分点,而且掉得越多的模型,生成 GSM8K 原题的概率越高。
6.3 实验:整个文件的泄漏——分数没变,但模型见过
测试集最常见的泄漏方式不是”一题一篇”,而是整个数据文件出现在网上:GitHub 仓库里的 JSON、论文的附录、题库网站。代码 05 §3:
-
泄漏的是分片文件,每片 10 题,按规范顺序排列,例如:
bench : she waters her iris in the morning . my friend feeds the cat . <sep> they grow … <sep> … -
测两种准确率:
- 评测格式:正常评测,每题单独给出;
- 文件语境:引导式测试(guided prompting,Golchin & Surdeanu 2024 的思路)。把题目放回泄漏文件里它原来的位置:前面是
bench :和同一分片里排在它前面的题与答案。
| 模型 | 评测格式:泄漏的题 | 文件语境:泄漏的题 | 文件语境:干净的题 |
|---|---|---|---|
| 干净 | 23.5% | 18.0% | 17.0% |
| 整个文件 ×16 | 23.0% | 23.5% | 20.0% |
| 整个文件 ×64 | 25.0% | 52.5% | 17.5% |
| 打乱顺序的文件 ×16 | 23.0% | 23.5% | 22.0% |
整个文件重复 64 次,模型在文件语境里答对了一半多,远超上限:它确实记住了。可是在正常的评测格式下,分数和干净模型一样。(README 记录了一个更极端的结果:重复 256 次,文件语境里 100%,评测格式仍是 23.5%。)
记忆和语境绑在一起:
- 模型记住的是”在
bench :和前面那几道题之后,接下来是什么”,而不是”这道题的答案是什么”。 - 换一种格式提问,记住的东西就”提取”不出来——和第 11 讲 §1、Allen-Zhu & Li 的”知识提取”是同一个现象。
两个推论:
- 泄漏不一定让分数注水(取决于泄漏的格式和评测的格式有多接近);
- “分数没有异常”不能证明”没有泄漏”。
6.4 实验:怎样检测
先看需要训练数据的 n-gram 查重(GPT-3、第 3 讲的做法):题目(上下文 + 答案)里有没有长度 n 的片段出现在训练数据里。泄漏的题在任何一种泄漏方式里都原样出现过。
| n | 泄漏的题被标出 | 干净的题被标出(误报) | 题目长度 ≥ n 的比例 |
|---|---|---|---|
| 8 | 100% | 64% | 100% |
| 10 | 100% | 26% | 100% |
| 13 | 50% | 0% | 51% |
花园句子高度模板化:n 取 8 时,六成干净的题也”出现过”(第 7 讲见过的误报)。n 取 13(GPT-3 的做法)时误报消失了,可是短于 13 个词的题根本没有 13-gram,一半的泄漏题查不出来。真实的基准也有大量短题(选择题的选项、简短的问答)。n 的选择是误报和漏报之间的权衡,而且这一切都要求能看到训练数据。
再看两种只需要模型给出的概率的”黑盒”方法:
- Min-K% Prob(§5.1):泄漏题 vs 干净题的分数 AUC。
- 顺序检验(Oren et al. 2024, Proving Test Set Contamination in Black Box Language Models):
- 原理:如果模型没见过这个基准文件,题目之间没有顺序可言,规范顺序和任何一种打乱的顺序是”可交换的”,它们的 log 概率没有系统差别。如果模型见过按规范顺序排列的文件,规范顺序的 log 概率会显著更高。
- 检验:对每个分片,把规范顺序的 log 概率和 50 种随机顺序比较,p 值 = (1 + 打乱后不低于规范顺序的次数) / 51。
- 好处:只需要序列的 log 概率;在”可交换”的假设下,假阳性率有严格的保证,不会冤枉干净的模型。
| 模型 | Min-K% AUC | 顺序检验:泄漏分片 p < 0.05 | 顺序检验:干净分片 p < 0.05 |
|---|---|---|---|
| 干净 | 0.540 | 0% | 5% |
| 一题一篇 ×4 | 0.651 | 10% | 0% |
| 一题一篇 ×16 | 0.983 | 5% | 0% |
| 整个文件 ×16 | 0.563 | 100% | 5% |
| 整个文件 ×64 | 0.585 | 100% | 10% |
| 打乱顺序的文件 ×16 | 0.562 | 5% | 0% |
- Min-K% 对”一题一篇 ×16”非常灵(被记住的题,最意外的那几个词也不意外了),对整个文件的泄漏只比瞎猜好一点。它只能检测”在评测格式下被记住”的题。
- 顺序检验对整个文件的泄漏非常灵:重复 16 次就抓住了全部 20 个分片,而这时分数根本没有注水。干净的分片只有 0~2 个被标出,和 5% 的名义假阳性率一致(练习 4)。但它对”一题一篇”和”打乱的文件”毫无办法:它检测的是”见过这个顺序”,没有顺序可言时就失效了。
6.5 实践中怎么办
没有一种检测方法能覆盖所有的泄漏方式。实践中的组合:
- 基准的发布方:
- 放金丝雀字符串(第 7 讲 §5.3);
- 不公开测试集的答案,或者干脆保留一个私有的测试集;
- 定期更新题目(LiveBench、LiveCodeBench 按月出新题,并标注题目的发布日期,可以只在模型训练截止日期之后的题上评测)。
- 模型的训练方:
- 训练前对已知的基准做去污染(n-gram 或嵌入相似度);
- 发布模型时报告污染分析,例如 Llama 2、GPT-4 的技术报告都有专门的章节。
- 使用基准的研究者:
- 在同分布的新题上复核;
- 对重要的结论做顺序检验、Min-K% 等检查;
- 把”可能的泄漏”作为结果解读的一部分。
7. 小结、常见误区、练习
7.1 小结
- 威胁模型:
- “安全”总是相对于”攻击者能接触到什么、想要什么、愿意付出多少”而言。
- 访问权限从聊天界面到全部权重依次增强;攻击目标可以用机密性、完整性、可用性分类。
- 防御要纵深,评价防御要假设攻击者知道防御。
- 安全训练的失效:
- 原因有两个:目标冲突和泛化不匹配。
- 标准的安全微调只改了回答的第一个 token(浅层对齐):普通请求的泄漏从 49% 降到 12%,带无害的指令或预填回答的开头就回到 25%~85%。
- 恢复样例把对齐做深,六种输入全部降到 0%。
- 无害的定制微调会抹掉浅层的对齐(12% → 36%),对深度对齐几乎没有影响。
- 提示注入:
- 根源是指令和数据共用一个通道;干净训练的模型四到五成的时候会执行数据里的指令。
- StruQ 的两部分缺一不可:攻击者写不出的保留 token,加上带注入的结构化指令微调。
- 系统层面要假设模型一定会被注入:最小权限、人工确认、控制流与数据流分离。
- 记忆:
- 记忆随重复次数、训练遍数和模型大小增长,而且不影响测试损失。
- 去重大幅减少记忆。
- DP-SGD 给出可证明的保证,代价是质量;对重复的数据保证会变弱。
- “原样说出”不等于”记住了”,要和没见过这条数据的参考模型比。
- 对齐把记忆藏起来,但没有删掉。
- 成员推断:
- 在只训练一两遍的预训练数据上几乎和瞎猜一样。
- 很多”成功”来自成员集和非成员集的分布差异,要用同分布的对照和盲基线。
- 一批文档的弱信号可以聚合成可靠的统计检验(数据集推断)。
- 基准泄漏:
- “超过贝叶斯上限”是泄漏的铁证;一题一篇、重复 16 次让分数从 23% 涨到 99.5%。
- 整个文件的泄漏不一定提高正常评测的分数,但引导式测试能发现它。
- 检测方法各有盲区:n-gram 查重要选 n、还要看得到训练数据;Min-K% 只看到评测格式下的记忆;顺序检验只看到规范顺序的文件。
7.2 常见误区
- “模型拒绝了这个请求,说明它是安全的”:要看换一种形式、预填回答的开头、微调之后还拒不拒绝(§2.3、§2.7)。
- “安全训练删除了危险的知识”:它只改变了”在什么情况下说出来”,而且常常只改了第一个 token(§2.4)。
- “在提示里写清楚’忽略数据里的指令’就能防住注入”:模型没被训练过区分指令和数据;攻击者也能伪造分隔符(§3.2、§3.3)。
- “验证损失没有异常,说明模型没有记住训练数据”:记住少量记录完全不影响验证损失(§4.3)。
- “模型逐字生成了训练集里的句子,所以它记住了训练数据”:常见的文本靠泛化也能生成(§4.6)。
- “用了差分隐私,所有数据都受保护”:保护的是”一篇文档”;重复多次的内容、分散在多篇文档里的个人信息保护得弱得多(§4.5)。
- “成员推断的 AUC 很高,说明方法有效”:先检查成员和非成员是不是同分布,再和盲基线比(§5.3)。
- “分数没有异常高,说明测试集没有泄漏”:记忆和语境绑在一起,泄漏可能完全不反映在分数上(§6.3)。
7.3 练习
练习 1(曝光度):
(a) 某个 4 位 pin 在 个候选里排第 3,曝光度是多少? (b) 如果模型完全没记住,真实 pin 的名次在 1~N 上均匀分布,期望曝光度是多少(N 很大)? (c) 为什么 Carlini et al. 用曝光度,而不只是看”能否原样补全”?
参考答案
(a) 比特。
(b) 期望曝光度 ,其中 。由 Stirling 公式 ,得 。所以期望曝光度约为 比特,和 N 无关——这就是 §4.2 说的”没记住时约 1.4 比特”。表里没记住的记录是 1~3 比特(每组只有 8 个用户,波动不小)。
(c) “能否原样补全”是一个 0/1 的量,只能看到记得最牢的那些。曝光度是连续的:
- 名次从 5000 升到 50(曝光度从约 1 比特升到约 7.6 比特)说明模型已经”部分记住”。攻击者只需要试几十个候选,就能以很高的概率猜中。
- 它还能比较不同的训练设置(§4.3 的表),而不用等到记忆强到能原样补全。
练习 2(群组隐私):§4.5 里 σ = 1.0 时 ε = 4.3(δ = 1e-5)。
(a) 一条重复了 32 次的记录,差分隐私的保证退化为多少? (b) 这说明了什么? (c) 怎样在不改变 DP-SGD 的前提下改善这种情况?
参考答案
(a) 群组隐私:如果算法对”相差一篇文档”是 -DP,那么对”相差 k 篇文档”是 -DP。k = 32 时 kε ≈ 138, 是一个天文数字,保证完全失去了意义。
(b) DP 的保证是针对”一个隐私单位”的。如果同一个秘密在数据里出现很多次,或者同一个人的信息分散在很多篇文档里(邮件、帖子、聊天记录),样例级的 DP 就保护不了它——这正是表里重复 32 次的记录仍有 4~7 比特曝光度的原因。
(c) 有两个办法:
- 先去重:让每个秘密只出现一次,§4.4 显示这本身就大幅减少记忆。
- 改变隐私单位:例如用户级 DP——把一个用户的所有数据作为一个单位来裁剪和加噪。这在实现上更难,对质量的影响也更大,因为一个单位的数据量变大了,裁剪丢掉的信息更多。
练习 3(隐私放大):用代码里的 compute_epsilon 或手算(δ = 1e-5,σ = 1):
(a) 不抽样(q = 1)只训练 1 步,ε 是多少?提示:此时每个整数阶 α 的 RDP 是 α / (2σ²)。 (b) q = 1 训练 100 步呢? (c) q = 0.01 训练 100 步、10000 步呢? (d) 解释为什么 DP-SGD 离不开”抽样”。
参考答案
(a) 对每个整数阶 α 计算 :α = 5 时 5.38,α = 6 时 5.30,α = 7 时 5.42。所以 ε ≈ 5.30(compute_epsilon(1.0, 1.0, 1) = 5.30)。一步不抽样的高斯机制就已经花掉了 ε ≈ 5。
(b) 100 步:,在 α = 2 时最小,为 100 + 11.51 = 111.5。保证完全没有意义。
(c) q = 0.01:100 步 ε ≈ 1.62,10000 步 ε ≈ 7.47(代码输出)。
(d) 每一步只有 1% 的数据参与,一篇文档在大多数步里根本不在批次里。攻击者不知道它在哪些步里出现过,所以每一步的隐私损失远小于不抽样时(大致按 q² 缩小,这就是”抽样放大”)。§4.5 的设置:批大小 256、8504 篇文档(q ≈ 0.03)、σ = 1、训练 272 步,ε ≈ 4.3。没有抽样放大,几百步的训练不可能得到有意义的 ε。
练习 4(顺序检验的 p 值):§6.4 的顺序检验每个分片用 50 种随机顺序。
(a) 一个分片能得到的最小 p 值是多少?如果想得到 p < 0.001,至少要多少种随机顺序? (b) 对一个干净的模型,在”可交换”的假设下,一个分片的 p < 0.05 的概率是多少? (c) 20 个干净分片里有 2 个或更多被标出的概率是多少?表里”整个文件 ×64”那一行干净分片有 10% 被标出,值得担心吗?
参考答案
(a) p = (1 + 打乱后不低于规范顺序的次数) / 51,最小是 1/51 ≈ 0.0196。要 p < 0.001,至少需要 999 种随机顺序(1/1000)。10 道题有 10! ≈ 363 万种顺序,足够多。Oren et al. 还把多个分片的结果合并成一个检验,能得到小得多的 p 值。
(b) 在可交换的假设下,规范顺序在 51 个顺序(自己加 50 种随机顺序)里的名次是均匀分布的(不考虑并列)。p < 0.05 等价于 p ≤ 2/51,也就是名次在前 2 名,概率是 2/51 ≈ 3.9%。
(c) 20 个分片独立,每个 3.9%:
- P(0 个) = 0.961²⁰ ≈ 0.452;
- P(1 个) = 20 × 0.039 × 0.961¹⁹ ≈ 0.367;
- P(≥ 2 个) ≈ 0.18。
所以 20 个干净分片里有 2 个被标出(10%)在零假设下并不罕见,不值得担心。相反,泄漏的分片 20 个里 20 个被标出,在零假设下的概率是 0.039²⁰——这才是”证明”。
练习 5(Min-K% 的直觉):某段 10 个 token 的文本,每个 token 的 log 概率是:
−0.1、−0.2、−3.0、−0.5、−0.05、−4.0、−0.3、−0.1、−0.2、−1.0
(a) 算平均 log 概率和 Min-20% 分数。 (b) 为什么 Min-K% 比平均损失更适合成员推断? (c) 它的弱点是什么?
参考答案
(a) 平均 = −9.45 / 10 = −0.945。最低的 20%(2 个)是 −4.0、−3.0,平均 −3.5。
(b) 一段没见过的文本,大部分 token 都容易预测(虚词、常见搭配),只有少数几个”意外”的 token(专有名词、数字、罕见的说法)有很低的概率。平均损失被大量容易的 token 稀释了。训练过的文本,恰恰是那几个意外的 token 的概率被抬高得最多(模型记住了”这里是 4.0 而不是别的”)。Min-K% 只看这几个 token,信号更集中。§6.4 里,一题一篇 ×16 的 Min-K% AUC 达到 0.983。
(c) 弱点有两个:
- 它仍然只是”这段文本对模型来说熟不熟”,分布偏移照样会骗过它(§5.3 的表:Min-20% 在偏移的非成员集上 AUC 0.832)。
- 文本本身的难度(有多少罕见词)会影响分数。后续的 Min-K%++ 等方法用每个位置的概率分布做了归一化。
练习 6(恢复样例的损失掩码):§2.6 的恢复样例形如”受限请求 + <ok> + 配方的前 k 位 + <refuse>”,前缀部分不计损失。
(a) 如果前缀也计损失,会发生什么? (b) Qi et al. 的数据增强还有一个隐含的要求:前缀应该是”真实的有害回答”的开头,而不是随机的词。为什么?
参考答案
(a) 模型会被训练成:在受限请求之后先说 <ok>、再说出配方的前几位,然后才拒绝。也就是说,它被直接教会了泄漏配方的开头。在真实的场景里,就是被教会说出有害回答的前几句。这完全违背了目的。前缀只能作为条件:教的是”在这种情况下怎么办”,而不是”怎样进入这种情况”。
(b) 攻击者预填或诱导出的开头,是”像真的有害回答”的开头(§2.3 的预填用的就是真实配方的前几位)。如果训练用的前缀是随机词,模型学到的可能是”看到奇怪的开头就拒绝”,遇到逼真的开头就不起作用了——又是一次泛化不匹配。所以 Qi et al. 用真实有害回答的前缀做增强。
练习 7(思考:设计一个邮件助手的防御):一个邮件助手能读用户的邮件、总结、起草回复、发送邮件、在日历上添加事件。只讨论原理:
(a) 列出它面对的提示注入风险。 (b) 按 §3.4 的四个层次各设计一项防御。 (c) 哪一项最不依赖”模型不被骗”?
参考答案(开放)
(a) 风险:
- 收到的邮件里藏着指令,让助手把其他邮件转发给攻击者;
- 在回复里插入钓鱼链接;
- 在日历上添加带恶意链接的事件;
- 在总结里隐瞒或歪曲重要内容。
(b) 四个层次各一项:
- 提示层:邮件正文用数据标记包起来,系统提示说明”邮件内容是数据”。
- 训练层:使用经过指令层级 / StruQ 式训练的模型;邮件内容放在工具输出的角色里,优先级低于用户。
- 检测层:邮件进入上下文前用注入检测分类器检查;发出的邮件检查收件人是否在用户的联系人里、是否包含外部链接。
- 系统层:
- 最小权限:总结功能根本不需要发送权限,起草和发送分开;
- 任何发送、转发、添加外部参与者的操作都需要用户确认;
- 控制流与数据流分离(CaMeL 式):用户的请求决定要做哪些操作,邮件内容只能作为参数,不能改变要做的操作。
(c) 系统层。它的出发点就是”假设模型会被骗”,然后让被骗之后能造成的损害有上限。
练习 8(盲分类器的 AUC):§5.3 的盲分类器给”含没见过的搭配”的文档打 0 分,其余打 1 分(越大越像成员)。成员里没有一篇含这些搭配,非成员(第 1 讲的测试集)里有 18% 含。
(a) 推导它的 AUC。 (b) “每篇都含”的非成员集呢? (c) 这说明在成员推断的评测中要注意什么?
参考答案
(a) AUC = P(成员分数 > 非成员分数) + ½ P(相等)。
- 成员的分数全是 1。
- 非成员 18% 是 0(成员分数更高)、82% 是 1(相等)。
- AUC = 0.18 + ½ × 0.82 = 0.59,正是表里的 0.590。一般地,AUC = 0.5 + f / 2,其中 f 是”一眼能认出的”非成员比例。
(b) f = 1,AUC = 1.0。
(c) 非成员集里只要有一小部分”一眼能认出”的样本,比如话题、日期、写作风格不同,任何方法都会因此得分。所以:
- 先用不看模型的盲分类器测一下非成员集”有多好认”;
- 评测成员推断方法时,成员和非成员要来自同一个分布(随机切分)。
练习 9(逐字相同的概率):用花园世界的真实分布估算:
(a) I eat the apple . <eos> 的概率约是 9.2 × 10⁻⁴,在 8000 篇独立采样的训练文档里期望出现几次?至少出现一次的概率是多少?
(b) 一个从没见过这些训练文档的模型生成了这篇文档,算”记住了训练数据”吗?
(c) 设计一个判断”模型是否记住了某篇训练文档”的方法。
参考答案
(a) 期望次数:8000 × 9.2 × 10⁻⁴ ≈ 7.4 次。至少一次的概率:。几乎可以肯定它在训练集里。可以用 A.WORLD.logprob("I eat the apple .".split() + ["<eos>"]) 验证。
(b) 不算。它在任何同分布的数据集里几乎都会出现,参考模型生成它的频率和目标模型一样(§4.6 的表:参考模型的”逐字相同”比例甚至更高)。“逐字相同”只说明这是一篇高概率的文档。
(c) 反事实的思路:比较”训练时包含这篇文档的模型”和”不包含它的模型”在这篇文档上的表现(损失或生成它的频率)。
- 差距大,才是记住了。
- 实践中训练很多个模型太贵,可以用一个同分布、不含它的参考模型近似(§5 的参考模型分数),或者用 zlib 等”文本本身有多容易”的度量校准。
- pin 记录是理想的例子:参考模型生成正确 pin 的概率是 10⁻⁴,目标模型说出了 215 个,差距一目了然。
7.4 延伸阅读
- 原课阅读:
- Wei, Haghtalab & Steinhardt (2023), Jailbroken: How Does LLM Safety Training Fail?
- Nasr et al. (2023), Scalable Extraction of Training Data from (Production) Language Models
- 安全训练与越狱:
- Qi et al. (2024), Safety Alignment Should Be Made More Than Just a Few Tokens Deep
- Qi et al. (2023), Fine-tuning Aligned Language Models Compromises Safety, Even When Users Do Not Intend To!
- Zou et al. (2023), Universal and Transferable Adversarial Attacks on Aligned Language Models(GCG)
- Jain et al. (2023), Baseline Defenses for Adversarial Attacks Against Aligned Language Models
- Anil et al. (2024), Many-shot Jailbreaking
- Zou et al. (2024), Improving Alignment and Robustness with Circuit Breakers
- Inan et al. (2023), Llama Guard
- 提示注入:
- Greshake et al. (2023), Not What You’ve Signed Up For: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection
- Chen et al. (2024), StruQ;Chen et al. (2025), SecAlign
- Wallace et al. (2024), The Instruction Hierarchy
- Hines et al. (2024), Defending Against Indirect Prompt Injection Attacks With Spotlighting
- Debenedetti et al. (2025), Defeating Prompt Injections by Design(CaMeL)
- 记忆与隐私:
- Carlini et al. (2019), The Secret Sharer
- Carlini et al. (2021), Extracting Training Data from Large Language Models
- Carlini et al. (2023), Quantifying Memorization Across Neural Language Models
- Kandpal et al. (2022), Deduplicating Training Data Mitigates Privacy Risks in Language Models
- Lee et al. (2022), Deduplicating Training Data Makes Language Models Better
- Zhang et al. (2023), Counterfactual Memorization in Neural Language Models
- Abadi et al. (2016), Deep Learning with Differential Privacy
- Mironov et al. (2019), Rényi Differential Privacy of the Sampled Gaussian Mechanism
- 成员推断:
- Shi et al. (2024), Detecting Pretraining Data from Large Language Models(Min-K% Prob)
- Duan et al. (2024), Do Membership Inference Attacks Work on Large Language Models?
- Das et al. (2024), Blind Baselines Beat Membership Inference Attacks for Foundation Models
- Maini et al. (2024), LLM Dataset Inference
- 基准泄漏:
- Oren et al. (2024), Proving Test Set Contamination in Black Box Language Models
- Golchin & Surdeanu (2024), Time Travel in LLMs: Tracing Data Contamination in Large Language Models
- Zhang et al. (2024), A Careful Examination of Large Language Model Performance on Grade School Arithmetic(GSM1k)
- Sainz et al. (2023), NLP Evaluation in Trouble
- 其他攻击:
- Carlini et al. (2023), Poisoning Web-Scale Training Datasets is Practical
- Hubinger et al. (2024), Sleeper Agents
- Carlini et al. (2024), Stealing Part of a Production Language Model
- 本仓库的相关内容:
- 第 3 讲(去重与去污染)
- 第 7 讲(测试集泄漏、金丝雀字符串、GSM-Symbolic)
- 第 9 讲(对齐的两种失效、多示例)
- 第 10 讲(工具调用的安全)
- 第 11 讲(RAG 的投毒与访问控制)
- 第 15 讲(红队测试)
- 第 18 讲(RLHF)
7.5 下一讲预告
第 17 讲是一次客座讲座:用 LLM 做推荐系统。推荐和语言建模有一个惊人的共同点:
- 用户的点击序列就像一个句子,“下一个物品”就像”下一个 token”。
- SASRec 把 Transformer 直接用在物品序列上。
我们会讨论三件事:
- 物品 ID 怎样 token 化;
- 怎样把一个 decoder LLM 改造成文本编码器(LLM2Vec);
- 用 LLM 做重排序时,逐点打分、成对比较、列表排序的差别。
我们会在一个合成的”花园集市”里训练一个序列推荐模型,并看看新物品(冷启动)该怎么办。