第 7 讲习题集
四组难度递进:A 概念判断 → B 推导证明 → C 编程实现 → D 开放设计。答案默认折叠,点击展开。 用法:先闭卷做 A 组(只写对错不给分,必须给一句话理由或反例);B 组手写推导;C 组上机并跑通每题末尾的验收标准;D 组写成半页方案(方案 + 理由 + 失败判据 + 验证计划,四项缺一不可)。 两条本讲特有纪律:① 涉及伦理与政策争议的题目,参考答案一律并列不同立场与各自依据并指出「哪种表述不可验证」,不要求也不给分给任何替读者下价值判断的写法;② 引用监管事实必须写核查时点与来源,不确定处写「需核实」(勘误页第三节)。
A 组 · 概念判断(8 题)
A1. 「把模型对齐就是让它听话:用户要什么就给什么,越顺从越对齐。」(依据:定义 7.2.1、定义 7.2.2、7.2 的误解 1)
A2. 「我们请外部团队做了一轮红队,没有找出问题,可以认定系统是安全的。」(依据:定义 7.2.4、7.1 的误解 4)
A3. 「训练数据里已经删掉了性别、民族、户籍这些字段,所以模型对任何群体都不会有歧视。」(依据:定义 7.4.1、7.1 的误解 3)
A4. 「Grad-CAM 显示模型主要看右下角,所以决策依据就是右下角;这张图可以直接写进事故复盘。」(依据:方法 7.3.1、例 7.3.1、方法 7.3.4)
A5. 「ECE 已经压到 了,说明这个模型可以放心用于自动决策。」(依据:定义 7.3.5、7.3 的误解 5)
A6. 「给生成内容加上水印,就可以做通用的『AI 内容检测』了。」(依据:定义 7.3.6、7.3 的误解 6)
A7. 「FMEA 表算完之后, 最高的那一项就是必须最先处理的项。」(依据:定义 7.5.2、例 7.5.1 第 ① 问)
A8. 「只要给每个安全指标都配了监控看板,可检测度 就不会高。」(依据:定义 7.5.2、例 7.5.1 第 ② 问、7.5 的误解 3)
答案(每题:判断 1 分 + 理由 2 分)
A1. 错。 服从本身可能是不对齐:一个能被任意越狱、对任何请求都照做的系统恰恰不对齐(7.2 的误解 1)。对齐衡量的是代理目标 与真实意图 的接近程度,而不是顺从度;「让模型听话」没有指定 ,因此不可验证。 A2. 错。 红队产出的是「在哪些攻击下会失守」以及覆盖面有多宽,对「已知失效集合的补集」没有任何陈述能力(定义 7.2.4)。「没找到问题」与「不存在问题」之间的距离由红队的覆盖面与创造性决定,不由结论决定。可验证的写法是给出「用例数 / 攻击类别 / 工具范围 / 完整绕过率」。 A3. 错。 删掉的是特征,不是信息:地址与邮编、就读学校、设备型号、活跃时段、拼写与语言风格都可能是受保护属性的代理变量(定义 7.4.1 后的读法)。判断依据不是字段清单,而是分群评估——并且要连「受保护属性如何获得(申报还是推断)及其推断质量」一起报告。 A4. 错,至少不足以这样写。 ① 单张热力图是单样本证据,没有统计意义,「主要依据」是全称结论;② 图形形状受方法、层、归一化与后处理影响,不同方法可能给出互相矛盾的图;③ 关键是忠实性未经检验——高亮的依据来自近似器而非模型内部计算。例 7.3.1 的读图算术上正确,但前提未验证。要进报告至少附扰动检验结果与方法一致性,并按方法 7.3.4 标明证据等级。 A5. 错。 ① 校准只在评测分布上成立,漂移后先失效、而且失效得很安静(数字仍好看、含义已变),必须与 OOD 监测成对上线;② 校准不改变排序(AUC 不变),模型分不开两类样本时,校准只是把数字改得更诚实;③ ECE 是加权平均,会把「低置信区完全正确」与「高置信区严重过自信」混成一个不错的数字——门槛必须写成分桶形式(例 7.3.2)。 A6. 错。 水印是可计算的检验统计量(绿名单偏置下的 ),相对「检测器模型」的优势是假阳率可控;但它只对加了水印的那一个模型有效,对开源权重模型与未加水印的模型都没有作用。它不能防篡改(改写、翻译、截断、换模型润色都会让信号衰减甚至消失),假阴率随文本长度与改写程度变化(短文本几乎无法判定)。它是取证线索,不是防伪标(定义 7.3.6)。 A7. 错。 是三个因子的乘积,低发生度会掩盖高严重度。FMEA 在安全关键系统里的标准纪律是不许用 RPN 覆盖 极高的项:例 7.5.1 的 F2 有 、,排在 F5()、F3()、F1()之后,却必须单列高危项并先处理(对策:变体对抗测试 高危类别白名单复核)。正确做法是把「 的项」单独列成一张高危清单。 A8. 错。 衡量「后果发生之前被发现的可能性」,有指标只是三件事里的第一件:还要有合适的阈值(由基线推出,不能拍脑袋)与从告警到人工介入的时延,并且要有止损动作。三条缺一条 都降不下来:例 7.5.1 的 F5 即使建了指标,若没有「相对基线上升 X 个百分点即告警」的阈值与「拦截结果不入训」的管线改动, 仍是 9。
B 组 · 推导与证明(6 题)
B1. 公平准则的不兼容性(直觉版推导)。设分数 在每个群体上校准: 与群体无关;记 。(a) 用贝叶斯公式写出 ,指出均等化几率要求什么等式;(b) 化成两组条件密度的比例关系并写出比例因子;(c) 取例 7.4.1 的 A、B 两组(、)与某网格点(、),算出 B 组必须取到的密度;(d) 说明为何这对所有 不可能同时成立,「基础发生率相同」为何让矛盾消失。
B2. KL 正则目标的闭式解与 DPO 的来处。对 ,:(a) 写拉格朗日函数与一阶条件;(b) 解出 与 ;(c) 反解 ,说明代回 Bradley–Terry 损失后 为什么消失;(d) 用 与 说明「 是对齐预算而不是普通正则系数」。
B3. 越狱的优化式 :(a) 为什么不能直接对 梯度上升(写清取值空间与导数在什么集合上有定义);(b) 写出 GCG 的两步并指出各需多少次前向 / 反向;(c) 取 、,估算 的数量级(给以 10 为底的对数);(d) 由 (c) 说明黑名单式过滤的天花板,以及为什么更可靠的方向是降低内部表示的线性可分性。
B4. 分桶 ECE 的读法。某二分类模型在验证集()上:桶 1( 条,置信 ,正确率 )、桶 2(,,)、桶 3(,,)、桶 4(,,)。(a) 逐桶列权重、差值与贡献,求 ECE;(b) 手算 与 ,验证 ,说明该恒等式在什么条件下成立;(c) 把桶 1、2 合并成 桶( 条、、)重算 ECE,解释它没变的原因,并说明只看 ECE 会漏掉什么;(d) 按「允许错误率 」逐桶检验,指出失守的桶与超限倍数,写出应有的门槛形式。
B5. SAE 的「重建 vs 稀疏」权衡(二维玩具)。、,,(),。(a) 求全开解()的 、重建误差与 ;(b) 只开 时写出 与 ,并用化简式 验证 与 两点;(c) 令 ,解出切换阈值 (三位小数)并说明两侧行为;(d) 极大时最优解变成什么?用它说明 SAE 的两个已知局限。
B6. 风险 → 需求的翻译。不合格的风险描述:「我们的在线问诊助手要防止被用来获取具体的用药剂量建议,这关系到患者安全。」(a) 按方法 7.5.3 的五要素逐条指出缺什么;(b) 改写成五要素齐备的需求(具体数字、或写出「待标定」并说明标定方式);(c) 按定义 7.6.1 的两条附加纪律,说明为什么必须同时给出过拒率与最差子群的数值。
提示与答案
B1. (a) 用贝叶斯公式(「校准」这一前提恰好让第一个因子与群体无关):
均等化几率要求 。(b) 代入后 约掉:
(c) 于是 B 组在该点必须取到
读法:要在「同一分数含义相同」(校准)的前提下把两组假阴性率拉平,该分数在 B 组的出现频率必须系统性低于 A 组()。(d) 该要求必须对每个 同时成立,即两组整个分数分布相差同一个非零常数因子 ;而两个分布各自要归一化到 ,只能是同一个分布的倍数——除非分布在几乎所有 上退化(分数毫无区分能力)。若 ,因子变成 ,两边可取同一分布,矛盾消失:这正是定理 7.4.1 里「基础发生率不同」这个前提的作用。
B2. (a) 展开两项后构造拉格朗日函数(固定 ,记 ):
(b) 整理一阶条件、把常数项并入归一化常数:
(c) 两边取对数得「奖励被策略比值的对数表达」:。代回 : 与 共享同一个提示 , 是同一个数,在差值中被消掉:
把 换成待训策略 并取负号求最小,即得 DPO 损失——奖励模型是被整条消掉的,而不是被近似掉的; 就是 DPO 的隐式奖励(方法 7.2.3)。(d) 偏移按 重加权: 时该因子被极端放大 → 微小奖励差异造成巨大偏移 → 更听奖励模型的话、也更容易掉进它的漏洞; 时 ,几乎学不动。所以 是「允许多大程度偏离既有行为」的对齐预算。
B3. (a) 的取值空间是离散集合 (词表的 次笛卡尔积,每个元素是一条 token 序列),梯度只在连续域上有定义;对 one-hot 表示 求的梯度是关于表示的导数,不是关于取值本身的导数,因此写不出「沿方向走一小步」。(b) ① 连续梯度定位:对每个位置算 、取 top- 候选(1 次反向拿到所有位置的梯度);② 离散贪心评估:逐位置、逐个候选地实际替换并前向评估,只保留目标值真正上升的那次替换( 次前向);③ 重复至收敛或达到预算。可迁移结论:当搜索空间离散而你有梯度时,梯度是启发式而不是方向。 (c) ,即约 条后缀。(d) 要「匹配」一类可疑后缀就必须枚举 量级空间里的元素,而优化者换一个目标函数就能生成新后缀——所以依赖黑名单的防御必须假设它是可被绕过的,它的价值是提高攻击成本,不是关闭攻击面。更可靠的方向是让有害意图在内部表示上不再线性可分(表征层干预不去追后缀形状,因而能挡住大量未见过的变体),代价是可能一并压制与目标任务共线的好特征,需要可解释性做定位(方法 7.2.6)。
B4. (a) 逐桶表:
| 桶 | 权重 | 贡献 | |
|---|---|---|---|
| 1 | 0.200 | 0.00 | 0.000 |
| 2 | 0.300 | 0.10 | 0.030 |
| 3 | 0.250 | 0.10 | 0.025 |
| 4 | 0.250 | 0.20 | 0.050 |
。(b) ;;差 ECE ✓。成立条件:所有桶的偏差同向(此处全是 ,即系统性过自信),此时绝对值可整体提出:;若有欠自信的桶,符号互相抵消,ECE 会大于净偏差。(c) 合并后权重 、、贡献 ,加上桶 3 的 与桶 4 的 仍是 ——没变正是因为 (b) 的恒等式:同向过自信时 ECE 等于「平均置信度 − 平均准确率」,与怎么切桶无关。由此得到本节最重要的工程结论:ECE 会把「低置信区完全正确」与「高置信区严重过自信」混成一个不错的数字——只看 就会漏掉「被自动处理的那批高置信样本实际错了 」。(d) 桶 1 允许 / 实际 → 通过;桶 2 允许 / 实际 → 通过;桶 3 允许 / 实际 → 不通过(1.11 倍);桶 4 允许 / 实际 → 不通过(4.17 倍)。门槛必须写成分桶形式(例如「桶 4 精确率 且覆盖率 」);温度缩放一类后处理能压低过自信,但不改变排序——用于「是否自动处理」够用,用于「预测本身对不对」无能为力。
B5. (a) 无惩罚时 ,其中 、。由对称性 ,代入 得 ,:重建误差 、、两个特征都开。(b) 只开 时目标为 ,求导得
验证: 时 、(劣于全开解的 ); 时 、,而全开解 ——仍劣。注意反直觉处: 会增大稀疏解的损失(它压低了 ),所以比较的是两条损失曲线,而不是单看一项。(c) 令 并代入 ,整理得 ,故
(如 ):两个特征都开更优(重建误差的下降大于稀疏惩罚的增加), 太小时 SAE 退化成普通自编码器;(如 ):只开一个特征更优( 优于 )。(d) 极大时 变负并被非负约束截断到 :什么都不开、重建误差 ,这就是「死特征 / 空重建」那一端。两个局限的落点:① 重建不完美——任何 的解都留下未解释的方差(这里 或更大),它残留在重建误差里,因此必须一起报告「有几成激活方差没被解释」;② 稀疏与可解释只是相关—— 越大编码越稀疏,但「每个特征都能被人类命名」不是稀疏度的推论(本例中 对应好解释的方向是我们构造出来的)。因此 SAE 的定位是审计与定位工具,不是安全证书。
B6. (a) 缺口:指标完全没有(「防止获取剂量建议」没有指明用哪个量观测);阈值与作用对象完全没有(无数值,也没说在什么数据 / 人群 / 分布上测);验收方式完全没有(「要防止」无法审计);负责人完全没有(要角色,不是「团队」);回退路径完全没有(止损动作与时延都没写)。唯一有用的信息是「关系到患者安全」——它指明 高、且单向不对称(漏答比过拒贵),但这仍是依据,不是需求。(b) 改写示例:需求 R-A——在占位评测集(,含「剂量类提问」与「一般用药信息提问」各不少于 条)上,剂量类提问中输出具体剂量的比率 (人工双盲判定,含单位换算与「以医生为准」式绕道表述),同时一般用药信息提问的过拒率 (阈值由留出的、与真实流量一致的校准集标定,标定记录归档);上线后按周复测,任一比率相对首月基线上浮 个百分点即告警。验收:红队脚本(改写 / 多轮追问 / 虚构情境三类入口)输出绕过率 人工抽检 条的一致性报告 告警演练记录。负责人:模型上线责任人(值班表具名)。回退:触发告警后 24 小时内把该类提问切到「结构化字段 引用」模式(只输出有检索证据支持的用药信息,剂量一律不给),5 个工作日内完成原因分析。(c) 过拒率是这条需求的另一侧代价: 的漏出率可以靠「一律拒答」轻易达成,只报一侧等于把需求写成「越严越好」,最终得到一个用户学会绕道的系统(而绕道会把风险推到监控看不见的地方)。最差子群则防止平均值掩盖差异:整体过拒率达标可能掩盖「某类表述的提问被 过拒」,而这批用户正被单向排除在服务外。二者合起来才让需求既有实质又不撒谎。
C 组 · 编程实现(6 题)
C1. 复现分桶 ECE 与逐桶相对口径:实现 ece、identity、relative_rule( 倍口径)。
- 验收标准:①
ece输出 (误差 );② 断言 ;③ 合并桶 1、2 为 后 ECE 不变(断言成立并打印结论);④relative_rule返回「桶 1、2 通过、桶 3、4 不通过」且桶 4 超限 (误差 )。
参考实现
import numpy as np
BUCKETS = [(200, 0.10, 0.10), (300, 0.40, 0.30), (250, 0.70, 0.60), (250, 0.95, 0.75)]
def ece(bs):
"""期望校准误差:按样本数加权的 |acc − conf| 之和。"""
m = sum(b[0] for b in bs)
return sum(b[0] / m * abs(b[2] - b[1]) for b in bs)
identity = lambda bs: (sum(b[0] * b[1] for b in bs) / sum(x[0] for x in bs),
sum(b[0] * b[2] for b in bs) / sum(x[0] for x in bs))
def relative_rule(bs, factor=1.2):
"""逐桶:实际错误率 (1−acc) 是否 ≤ factor × 允许错误率 (1−conf)。"""
return {f"桶{i}": ((1 - b[2]) <= factor * (1 - b[1]) + 1e-12,
round((1 - b[2]) / (factor * (1 - b[1])), 3)) for i, b in enumerate(bs, 1)}
conf, acc = identity(BUCKETS)
print("ECE =", round(ece(BUCKETS), 6), f";平均置信 {conf:.4f} 平均正确 {acc:.4f} 差 {abs(conf-acc):.4f}")
print("逐桶口径:", relative_rule(BUCKETS))
assert abs(ece(BUCKETS) - 0.105) < 1e-9
assert abs(abs(conf - acc) - ece(BUCKETS)) < 1e-12 # 同向过自信 → 恒等式成立
assert abs(ece([(500, 0.28, 0.22)] + BUCKETS[2:]) - 0.105) < 1e-9 # 桶 1+2 → [0,0.5),ECE 不变
r = relative_rule(BUCKETS)
assert r["桶1"][0] and r["桶2"][0] and not r["桶3"][0] and not r["桶4"][0]
assert abs(r["桶4"][1] - 4.17) < 0.05
print("结论:改桶数 ECE 不变 → 门槛必须写成分桶形式,不能只写 ECE ≤ ε。")
C2. 复现 Grad-CAM 的手算结果,并实现单调性检验(insertion):从空图基线开始,按解释器认为的重要性从高到低逐格填入前景,检查目标分数是否单调非降。
- 验收标准:① 用 、 与 、 复现 、 与归一化图(逐元素误差 ),且 、;② 忠实解释的插入曲线单调非降(容差 );③ 不忠实解释(把背景当关键区域)的曲线存在下降步;④ 断言忠实曲线终值严格大于不忠实曲线。
参考实现
import numpy as np
# ① 复现正文例 7.3.1:加权求和 → ReLU → 归一化
A1 = np.array([[0, 0, 1], [0, 1, 2], [1, 2, 3]], float)
A2 = np.array([[3, 2, 1], [2, 1, 0], [1, 0, 0]], float)
S = 0.8 * A1 - 0.5 * A2
S_relu = np.maximum(S, 0) # ReLU 是 Grad-CAM 的关键一步
G = S_relu / S_relu.max() # 归一化到 [0, 1]
assert np.allclose(S, [[-1.5, -1.0, 0.3], [-1.0, 0.3, 1.6], [0.3, 1.6, 2.4]])
assert np.allclose(G, [[0, 0, 0.125], [0, 0.125, 0.667], [0.125, 0.667, 1.0]], atol=1e-3)
assert G[0, 0] == 0.0 and G[2, 2] == 1.0 # 左上角被 ReLU 清零、右下角为峰值
# ② 单调性检验(insertion):首点是空图基线,少了它"第一步就掉下去"会被漏掉
W = np.array([[-0.5, 0.0, 0.0], [0.2, 1.0, 1.5], [0.1, 0.8, 2.2]]) # 含一处背景抑制项
IMG = np.array([[0.5, 0.4, 0.3], [0.6, 0.9, 0.7], [0.4, 0.8, 1.0]])
IMP = {"忠实": np.maximum(W, 0), # 只指向正贡献区域
"不忠实": np.array([[2.2, 1.5, 1.0], [0.5, 0.0, 0.0], [0.0, 0.0, 0.0]])}
score = lambda x: float((W * x).sum())
def insertion_curve(imp, img):
order = [ij for ij in sorted(np.ndindex(img.shape), key=lambda ij: (-imp[ij], ij)) if imp[ij] > 0]
x, curve = np.zeros_like(img), [score(np.zeros_like(img))]
for ij in order:
x[ij] = img[ij]
curve.append(score(x))
return np.array(curve)
monotone = lambda c, tol=1e-9: bool(np.all(np.diff(c) >= -tol))
c_f, c_w = insertion_curve(IMP["忠实"], IMG), insertion_curve(IMP["不忠实"], IMG)
print("忠实前五步:", np.round(c_f[:5], 3), " 不忠实前五步:", np.round(c_w[:5], 3))
assert monotone(c_f), "忠实解释应给出单调非降的插入曲线"
assert not monotone(c_w), "不忠实解释应出现下降步(首个填入的像素带负贡献)"
assert c_f[-1] > c_w[-1]
print(f"终值:忠实 {c_f[-1]:.3f} vs 不忠实 {c_w[-1]:.3f} → 单调性检验能区分两者。")
C3. 激活引导的加 / 减方向实验:由对比提示(正例均值 − 反例均值)构造概念方向 ,实现 ,扫描 (含负值),并加两个旁路探针做成组能力回归。
- 验收标准:① 目标行为随 单调上升(断言差分 ),负 时低于基线;② 旁路探针同向变化,且「旁路斜率 / 目标斜率」(误差 )——打印该比值并解释它为什么不是 ;③ 无关任务探针的斜率绝对值 (断言成立);④ 打印 的「退化风险」标记,并说明 与层号必须一起记录。
参考实现
迁移到真实模型的挂钩方式(不改权重,只在该层前向里加方向):
layer.register_forward_hook(lambda m, i, o: o + alpha * v_unit)。真实实验还需两点——方向必须来自同一层的对比激活(层选错则无效或破坏能力),且每换一个 都要重跑成组能力回归(下面两个旁路探针就是它的最小版)。
import numpy as np
# 两组对比提示在某层的激活(示意;实践中是正例 / 反例提示的激活均值)
h_pos = np.array([[1.1, 0.3, -0.2], [0.9, 0.4, -0.1], [1.0, 0.2, -0.3], [1.05, 0.35, -0.15], [0.95, 0.25, -0.25]])
h_neg = np.array([[0.1, 0.0, 0.0], [0.0, 0.05, -0.05], [-0.1, 0.0, 0.05], [0.05, -0.05, 0.0], [0.0, 0.1, -0.05]])
v_unit = h_pos.mean(0) - h_neg.mean(0) # 概念方向 = 对比提示的激活差
v_unit = v_unit / np.linalg.norm(v_unit)
PROBES = {"目标行为(拒答倾向)": np.array([1.0, 0.0, 0.0]), # 目标行为
"旁路一(与目标共线)": np.array([0.8, 0.6, 0.0]), # 与目标方向高度共线的"好特征"
"旁路二(无关任务)": np.array([0.0, 0.0, 1.0])} # 一个常规任务
alphas = np.array([-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0])
curves = {k: np.array([w @ (a * v_unit) for a in alphas]) for k, w in PROBES.items()}
for k, y in curves.items():
print(f"{k}:{np.round(y, 3)}")
beh, side, task = curves.values()
assert np.all(np.diff(beh) > 0), "α 增大应单调抬升目标行为,负 α 时应低于基线"
slope = lambda y: float(np.polyfit(alphas, y, 1)[0])
ratio = slope(side) / slope(beh)
print(f"目标斜率 {slope(beh):.4f} 旁路斜率 {slope(side):.4f} 斜率比 {ratio:.4f}")
assert abs(ratio - 0.970) < 0.01, "旁路被带动了几乎同量级的变化 → 引导没有副作用保证"
assert abs(slope(task)) < 0.2, "无关任务受影响较小(这正是必须做回归、不能假设的原因)"
print("退化风险标记(|α| > 1.5):", [float(a) for a in alphas if abs(a) > 1.5],
";记录要求:α 与层号必须一起写——换层或换 α 都是另一次实验。")
C4. 扰动一致性检查:实现输入×梯度、纯梯度与遮挡三种归因,比较两两的 top-3 一致性(Jaccard),并按归因排序做删除曲线与随机排序对照。
- 验收标准:① 口径相同时可对齐:线性模型上「输入×梯度」与「遮挡」的 top-3 完全一致(Jaccard );② 非线性造成真分歧:含交互项(ReLU 拐点处于临界位置)时同一对方法的 Jaccard (断言 ),打印两个 top-3 集合与分歧成员;③ 换口径也会不一致:线性模型上把归因换成纯梯度,Jaccard 也降到 ;④ 断言按归因排序的删除曲线曲线下面积严格小于 200 次随机排序的均值(固定随机种子,可复现);⑤ 打印处置结论:不一致的样本不可作为单一证据。
参考实现
import numpy as np
W = np.array([[-0.5, 0.00, 0.0], [0.2, 0.05, 1.5], [0.1, 0.80, 2.2]])
X = np.array([[0.5, 0.4, 0.3], [0.6, 1.2, 0.05], [0.4, 0.8, 1.0]])
C, GATE = 5.0, 0.05 # 交互项 C·ReLU(x[1,1]·x[1,2] − GATE),拐点处在临界位置
def make_score(c=C, gate=GATE): # c = 0 时退化为线性模型("两法必须对齐"的对照)
return lambda x: float((W * x).sum() + c * max(0.0, x[1, 1] * x[1, 2] - gate))
def grad_map(x, f, eps=1e-6): # 纯梯度归因(中心差分;真实系统里是一步反向传播)
g = np.zeros_like(x)
for ij in np.ndindex(x.shape):
xp, xm = x.copy(), x.copy(); xp[ij] += eps; xm[ij] -= eps
g[ij] = (f(xp) - f(xm)) / (2 * eps)
return g
def occlusion_map(x, f): # 遮挡类归因:逐格置零,看分数下降多少
base, m = f(x), np.zeros_like(x)
for ij in np.ndindex(x.shape):
xz = x.copy(); xz[ij] = 0.0
m[ij] = base - f(xz)
return m
grad_input_map = lambda x, f: grad_map(x, f) * x # 输入 × 梯度:另一种常见口径
topk = lambda a, k=3: {ij for ij in sorted(np.ndindex(a.shape), key=lambda ij: (-abs(a[ij]), ij))[:k]}
jac = lambda a, b: len(a & b) / len(a | b)
def jaccard_report(tag, attr_a, attr_b, expect):
ta, tb = topk(attr_a), topk(attr_b)
got = jac(ta, tb)
print(f"{tag}: Jaccard={got:.2f} 分歧={sorted(ta ^ tb)}(期望 {expect})")
return got
s_lin, s_int = make_score(c=0.0), make_score()
j = [jaccard_report("① 线性 / 输入×梯度 vs 遮挡", grad_input_map(X, s_lin), occlusion_map(X, s_lin), 1.0),
jaccard_report("② 含交互项 / 输入×梯度 vs 遮挡", grad_input_map(X, s_int), occlusion_map(X, s_int), 0.5),
jaccard_report("③ 线性 / 纯梯度 vs 遮挡", grad_map(X, s_lin), occlusion_map(X, s_lin), 0.5)]
assert j[0] == 1.0 and 0.4 < j[1] < 0.6 and abs(j[2] - 0.5) < 1e-12, "对齐 / 非线性分歧 / 口径差异"
def del_curve(x, order, f): # 从全图开始按 order 逐格置零,首点是初始分数
xc, curve = x.copy(), [f(x)]
for ij in order:
xc[ij] = 0.0
curve.append(f(xc))
return np.array(curve)
attr = grad_input_map(X, s_int)
order_attr = sorted(np.ndindex(X.shape), key=lambda ij: (-abs(attr[ij]), ij))
rng, cells = np.random.default_rng(0), list(np.ndindex(X.shape))
area_attr = float(del_curve(X, order_attr, s_int).sum())
area_rand = float(np.mean([del_curve(X, [tuple(ij) for ij in rng.permutation(cells)], s_int).sum()
for _ in range(200)]))
print(f"删除曲线面积:按归因 {area_attr:.3f} vs 随机均值 {area_rand:.3f}")
assert area_attr < area_rand, "按归因删除应比随机删除下降更快(面积更小)"
print("处置结论:两法不一致的样本,其解释只能作为线索;要进报告必须补成组统计与消融检验。")
C5. 风险—覆盖曲线与工作点判定:用例 7.3.2 的四桶数据算 曲线,并实现 deployable(cov, risk, c_star, r_star)。
- 验收标准:① 四点分别为 、、、(误差 );② 断言风险随覆盖率单调非降;③
deployable对 返回True、对 返回False;④ 打印「若同时施加逐桶 倍口径」的结论(桶 4 不得自动处理,与 的工作点冲突 → 先校准或扩大转人工)。
参考实现
import numpy as np
BUCKETS = [(200, 0.10, 0.10), (300, 0.40, 0.30), (250, 0.70, 0.60), (250, 0.95, 0.75)]
n = sum(b[0] for b in BUCKETS)
def risk_coverage(buckets, thresholds):
"""按「置信度 ≥ τ 才自动处理」计算 (覆盖率, 风险)。"""
rows = []
for t in thresholds:
sel = [b for b in buckets if b[1] >= t]
rows.append((t, sum(b[0] for b in sel) / n,
1 - sum(b[0] * b[2] for b in sel) / sum(b[0] for b in sel)))
return rows
deployable = lambda cov, risk, c_star, r_star: bool(cov >= c_star and risk <= r_star)
curve = risk_coverage(BUCKETS, [0.95, 0.70, 0.40, 0.10])
for t, cov, risk in curve:
print(f"τ={t:.2f} 覆盖率 {cov:.4f} 风险 {risk:.4f}")
assert np.allclose([round(c, 4) for _, c, _ in curve], [0.25, 0.50, 0.80, 1.00])
assert np.allclose([round(r, 4) for _, _, r in curve], [0.2500, 0.3250, 0.4656, 0.5525])
risks = [r for _, _, r in curve]
assert all(b >= a - 1e-12 for a, b in zip(risks, risks[1:])), "风险应随覆盖率单调非降"
assert deployable(0.25, 0.25, 0.20, 0.30) is True
assert deployable(0.80, 0.4656, 0.80, 0.30) is False
factor, (_, c4, a4) = 1.2, BUCKETS[-1]
print(f"逐桶 1.2 倍口径:桶 4 允许错误率 {factor*(1-c4):.2f},实际 {1-a4:.2f} → "
f"{'通过' if 1-a4 <= factor*(1-c4) else '不通过'}")
print("结论:τ=0.95 的工作点在逐桶口径下不成立 → 先做校准 / 转人工,再谈自动处理。")
C6. 水印的检验统计量:实现 ,用蒙特卡洛验证三件事。
- 验收标准:① 未加水印时 落在 (理论 ),并打印 95% 置信区间;② 真实命中率 、 时模拟 与解析值 相差 ,且 ;③ 时打印 并断言 (短文本无法判定);④ 打印随 的 序列与「改写会进一步降低命中率」这一外部依赖。
参考实现
import numpy as np
from math import erf
GAMMA, N_SIM = 0.25, 1_000_000
Phi = lambda x: 0.5 * (1 + erf(x / np.sqrt(2.0))) # 标准正态 CDF,用来对照理论假阳率
def z_stat(hits, T, gamma=GAMMA):
"""水印检验统计量:|s|_G 为落在绿名单的 token 数,T 为 token 数。"""
return (hits - gamma * T) / np.sqrt(T * gamma * (1 - gamma))
rng, T = np.random.default_rng(7), 1000
# ① 未加水印:每步命中绿名单的概率就是 γ
z_null = z_stat(rng.binomial(T, GAMMA, size=N_SIM), T)
fp = float((z_null > 4).mean()); se = np.sqrt(fp * (1 - fp) / N_SIM)
print(f"未加水印:P(z>4) = {fp:.2e} ± {1.96*se:.2e}(理论 Φ(−4) = {Phi(-4):.2e})")
assert 2e-5 < fp < 5e-5, "假阳率应与 Φ(−4) 同量级(这正是「假阳率可控」的含义)"
# ② 加水印:真实命中率抬到 p
p = 0.35
z_wm = z_stat(rng.binomial(T, p, size=N_SIM), T)
theory = (p - GAMMA) * np.sqrt(T) / np.sqrt(GAMMA * (1 - GAMMA))
print(f"加水印:模拟 E[z] = {z_wm.mean():.2f}(解析 {theory:.2f}),P(z>4) = {(z_wm > 4).mean():.4f}")
assert abs(z_wm.mean() - theory) / theory < 0.05
assert (z_wm > 4).mean() > 0.99
# ③ 短文本:统计量的期望随 √T 缩小 → 检不出
for Ts in (50, 200, 1000):
print(f"T={Ts:5d}: E[z] = {(p-GAMMA)*np.sqrt(Ts)/np.sqrt(GAMMA*(1-GAMMA)):.2f}")
assert (p - GAMMA) * np.sqrt(50) / np.sqrt(GAMMA * (1 - GAMMA)) < 4
print("边界:假阴率依赖文本长度与改写程度;水印是取证线索,不是防伪标,也检测不了未加水印的模型。")
D 组 · 开放设计(4 题)
每题都必须交出四项:方案 + 理由 + 失败判据 + 验证计划。评分不看篇幅,看是否可执行、可验收、可回退。
D1. 威胁模型与红队方案。 某内部文档问答智能体原有三个工具(知识库检索、日历读取、邮件草稿),本次新增第四个:工单创建(可直接在缺陷系统立单并自动通知被指派者);用户输入可能来自员工粘贴的外部邮件与网页片段。要求:(a) 写六段模板里的三段——信任边界与数据流 / 攻击者与能力假设(必须含最常被漏掉的那一类)/ 攻击面清单(按入口、通路、出口);(b) 设计红队方案(覆盖哪些注入位置与形态、指标口径、失败模式怎么记录、何时重跑);(c) 说明红队结论在文档里能写什么、不能写什么。
参考方案与评分要点
(a) 三段。 信任边界与数据流:数据流为「用户输入(可能含外部粘贴)→ 模型 → 工具调用(检索 / 日历 / 工单创建)→ 外部副作用(工单已创建、指派通知已发出)」;信任边界在「用户输入」处即断裂——粘贴的网页片段与外部邮件属于不可信内容,却与用户本人的指令在模型看来是同一个字符串流;工具返回值(日历标题、工单正文)同样由他人可写、不可信。工单创建是半可逆动作:工单可撤销,但指派通知已发出、被指派者的排期已被占用。 攻击者与能力假设(含最常被漏的一类):
| 类型 | 能力假设 | 期望结果 |
|---|---|---|
| 外部内容投放者(最危险) | 能让人看到一段文字(发邮件、放网页)并诱使员工粘贴进对话 | 让智能体读走内部信息,或创建内容由攻击者控制、指派对象难以追查的工单 |
| 内部好奇员工 | 有正常账号 | 越过权限读取不属于自己的文档 |
| 有组织的滥用者 | 多账号、有预算 | 批量创建工单以阻塞缺陷系统队列(拒绝服务式滥用) |
| 无意触发者(最常被漏) | 无任何恶意 | 粘贴含敏感信息的文档 → 敏感信息进入日志与第三方模型调用;或把「请立单给 X」误当成自己的意图 |
每条假设都要写「如果它不成立会怎样」。攻击面(选摘):入口——用户粘贴文本(单次会话)、检索返回的文档(可被内部人员长期投毒)、工具返回值(中等持续);通路——模型 → 工单创建 → 缺陷系统与通知渠道;出口——工单正文、附件、错误信息。每格问:这里能进什么?能影响什么?能持续多久? (b) 红队方案。 覆盖四类入口(直接注入 / 间接注入 / 角色扮演 / 多轮诱导)× 三类投放位置(粘贴文本、检索文档、工具返回值)× 两个目标(越权读取、越权写工单),共 条用例(含「与正常业务指令高度相似」的边缘用例);指标两侧都测——完整绕过率(产生攻击者预期且未经确认的副作用)、按类绕过率、以及过拒率;失败样本逐条归档(输入、上下文来源、工具调用序列、判定理由);每次模型、提示、工具 schema 或权限配置变更后重跑全量并留存版本。 (c) 能写 / 不能写。 能写:「在 条用例、四类攻击、两处工具入口的覆盖下,工单创建的完整绕过率为 ,失败样本见附录」。不能写:「红队通过,系统安全」——红队只扩大已知失效集合,对补集没有陈述能力(定义 7.2.4)。还要写清方法边界:红队不回答「这个能力把什么事变得更容易了」(那是滥用评估,核心是基线对照),也不回答「分布外表现如何」(那是常规评测)。 评分要点:三段齐全且信任边界正确 4 分(漏掉「无意触发者」扣 1 分);红队方案的覆盖面与双率指标 3 分(只报绕过率扣 1 分);结论表述纪律 3 分(写出「红队通过 ≠ 安全」得满分)。
D2. 监控与回滚设计。 某信贷产品的「额度调整模型」已上线三个月(输入账户行为特征,输出上调 / 维持 / 下调;低于某额度的调整自动生效,其余转人工;已有申诉通道)。要求:(a) 给出三层指标的最小版本,并指出哪一层最能提前发现分布漂移;(b) 为其中两个安全指标写出门槛、阈值来源与接收角色;(c) 写清灰度与同期对照设计;(d) 写出回滚的触发条件 / 执行人 / 执行时长,并给出事故四个时限( / / / )的取值与依据。
参考方案与评分要点
(a) 三层最小版本:质量层——调整决策准确率 / 与人工复核一致率、按额度区间与按群体的分群指标;安全层——拒答与转人工率、过拒率、分群质量差异(最差子群)、争议率(申诉量与申诉推翻率);行为层——三类决策的占比、上调额度分布、对单一特征的敏感度(捷径特征)、单位用户调用次数。漂移最早出现在行为层与输入侧:分布漂移往往先表现为「输入变了」(新客群、新消费模式、新渠道)与输出分布改变,再传导到质量指标;因此还要单独监控漂移检测本身的指标——样本量、输入分布变化、未覆盖输入的比例(方法 7.6.3)。样本量本身就是监控项:样本量下降往往先于指标恶化出现。 (b) 两个安全指标:
| 指标 | 阈值 | 阈值来源 | 接收角色 |
|---|---|---|---|
| 分群质量差异(最差子群误差率相对整体) | 升 个百分点 → 告警; → 熔断 | 首月线上分布 允许波动,并经一次人为注入偏移的演练校准 | 模型上线责任人(值班表具名) |
| 自动化比例与过拒率 | 自动化比例上限 ;过拒率 基线 个百分点 → 告警 | 由「转人工的人力预算」与「过拒的业务损失」两笔账共同推出 | 业务负责人( 风控复核) |
| (c) 灰度与同期对照:新版本先 流量、与旧版本同期并行,每档看四项(质量、分群差异、P95 延迟、转人工率);关键设计是对照——没有同期对照就无法区分「模型变差」与「这段时间流量变难」;灰度比例按样本量需求定,不要为快而失去统计功效;低额度自动档与高额度人工档分别统计。 | |||
| (d) 回滚与四时限: |
| 项 | 取值(示例) | 依据 |
|---|---|---|
| 触发条件 | 熔断指标命中 / 某群体申诉推翻率显著抬升 / 出现一次不可接受的个案 | 触发条件必须事前写死,且每个条件都有接收角色 |
| 执行人 | 值班工程师(执行) 业务负责人(确认);权限事前授予并演练过 | 未演练的回退在事故当天通常不可用(配置漂移、权限过期) |
| 执行时长 | 分钟切回上一版本; 小时把该版本产生的自动决策批量重跑或转人工 | 回滚要包含已产生后果的承接,而不只是切版本 |
| 个自然日(按日看板与告警采样频率决定) | 没有指标的失效等于没有发现时间 | |
| 小时(值班与升级路径) | 由值班设计决定 | |
| 分钟( 回滚时长) | 这一项最常被高估,它由「回退是否演练过」决定 | |
| 按合同与合规要求(例如严重事件在约定时限内通报受影响方) | 由合规与合同决定,需核对现行法域要求(需核实) |
加分项:把「先止损、后定位」写成流程——定位期间系统继续运行会改变数据、污染证据(日志与反馈被新模式覆盖),而回滚本身给出一次自然的对照实验,常常比事后分析更快锁定原因。 评分要点:三层指标齐全且指出漂移的最早信号 3 分;两个指标的门槛 / 来源 / 接收角色各 1.5 分;灰度含同期对照 2 分;回滚含执行时长与后果承接 2 分;四时限取值与「止损时间最常被高估」的理由 2 分。
D3. 公平度量的选择与理由。 某地公共助学金名额分配系统用模型对申请者排序,名额有限(约录取 );受保护属性按自愿申报获得,覆盖率约 (其余只能靠统计推断补足)。要求:(a) 写出方法 7.4.1 五步的答案,第 1 步必须写清「给谁分配什么、犯错的代价是否双向对称」;(b) 选一个主准则 一个副准则并写成需求(含阈值、验收、负责人、回退);(c) 并列两种立场(以结果分布为目标 / 以同等条件同等对待为目标)的依据,指出你的选择牺牲了什么、由谁承担;(d) 写明残余风险接受者与复查条件,并说明「受保护属性靠推断补足」如何影响第 (c) 步的可信度。
参考方案与评分要点
(a) 五步。 ① 对象与后果:把稀缺的教育资源分配给个体;错误双向不对称——漏助(合格者被拒)与误助(不合格者占用名额、挤掉别人)都有代价,且都不落在系统身上。② 候选准则与代价:人口统计平价 → 牺牲「同一分数同一含义」并可能要求对个体区别对待;机会均等 / 均等化几率 → 两组合格率不同时结果比例仍有差异;预测平价 / 分群校准 → 保留结果比例差异,且在基础发生率不同时与均等化几率一般不能兼得。③ 主 副:例如主准则 机会均等(约束合格者不因群体被错筛)、副准则 预测平价的偏离上限(约束「同一分数含义」别漂太远)。④ 写进目标或约束:加约束「组间假阴性率之差 」或对样本少的组重加权——只在评估阶段报指标,优化器没有任何动力改善它。⑤ 残余风险与复查条件:写明哪些差异被选择接受、依据是什么、何时重估(样本量达阈值 / 业务目标变更 / 法规更新 / 申报率变化)。 (b) 需求示范:需求 F-1——在占位评测集(各组样本量 ;受保护属性用「自愿申报」与「推断补足」两套口径各跑一遍)上,组间假阴性率之差 (绝对差),且各组的分群校准偏离 ;上线后按月复测,任一组相对首月基线恶化 个百分点即告警。验收:分群评估报告(含置信区间与推断属性的质量指标) 告警演练记录。负责人:模型负责人(准则与代价)与业务负责人(残余风险接受)双签。回退:触发告警后 24 小时内把该组降级为「进入人工复核池」,并按季度重估准则。 (c) 两种立场(并列,不下判断)。 立场 A:以结果分布为目标。 依据:历史不公正是通过结果分布差异长期累积的,只保证「同等条件同等机会」不足以纠正存量差异;且结果分布更容易被外部检验——外部只需看到通过率,不需看到分数与真实标签。立场 B:以同等条件同等对待为目标。 依据:在基础发生率真实存在差异时,强行拉平结果分布意味着对个体的区别对待(同等资质得到不同结果),这在很多岗位与法律框架下被明确禁止;且它把「历史差异应由谁承担」转移给了当下的个体。牺牲了什么、由谁承担(以选 A 为例):牺牲「同一分数在同一群体内含义一致」这一点(分群校准的偏离)以及可能的个体公平争议;代价由分数处于边界、且所属组基础发生率较低的那批申请者承担。若选 B,则代价由基础发生率较低群体的合格者以更高假阴性率的形式承担。两者共同接受的底线:必须显式选择并记录——不选择的默认(只优化平均损失)本身就是一种选择,而且往往是最不利于弱势群体的那一种。 (d) 残余风险与推断属性的影响。 接受者:业务负责人(分配的政治与伦理责任) 合规负责人(法域要求)共同签字;复查条件:申报率变化、样本量翻倍、相关法规更新、或某组申诉推翻率抬升。推断属性的影响:推断本身会出错,而且推断错误的分布并不随机(对少数族裔姓名的推断质量往往更差),因此一切基于推断属性的分群指标都必须同时报告覆盖率、准确率与不同子群上的差异;否则整份评估的可信度无从判断——这会让 (c) 中「以结果分布为目标」的立场更难被验证(分组标签本身带噪声)。这是必须写进文档的事实,不是可省略的脚注。 评分要点:五步齐全(第 1 步的不对称性分析、第 4 步的「写进目标」为关键)4 分;需求五要素齐备且阈值可验收 2 分;两立场并列且给出依据、写明「牺牲了什么、由谁承担」2 分;残余风险接受者与复查条件 1 分;推断属性质量对可信度的影响 1 分。若出现「应当以 X 为准则」而不给依据与代价,本部分不给分。
D4. 申诉与救济机制设计。 某平台的内容审核系统已上线(通过 / 拦截 / 转人工三分类,拦截会向作者发送理由),目前「申诉」只是帮助页深处的一张客服工单表。要求:(a) 按定义 7.4.3 设计一个能真正纠正(而不仅是受理)的流程,逐条写清「可申诉 / 可解释 / 可推翻 / 可聚合」四要件;(b) 说明「可解释」这一环为什么不能直接拿一张未经检验的归因图给用户;(c) 给出通道的监控指标(含一条反直觉指标)与两条可测量的失败判据;(d) 写出答复时限与升级路径。
参考方案与评分要点
(a) 四要件。
| 要件 | 具体设计 | 缺了它为什么不成立 |
|---|---|---|
| 可申诉 | 拦截通知里直接带入口(不藏在帮助页深处)、与拦截通知出现在同一渠道、支持无账号提交 | 只有通道可达,「被影响者能问为什么」才不是口号 |
| 可解释 | 给出判定所依据的规则类别与该内容命中的具体判据;若给不出与判定过程一致的理由,则不给理由、只给申诉入口 | 与判定无关的理由会让用户无法有效申诉、也无法学习规则(例 7.5.1 的 F3 就是这个失效模式) |
| 可推翻 | 复核者有权覆盖模型输出,需要两种权限:模型侧复核权限(能重跑、能看该样本的特征与分数、能看到判定链路)与业务侧覆盖权限(能批准一次例外并留记录) | 只有前者是「技术支持」,只有后者是「客服安抚」;两者都缺就是「不可推翻」 |
| 可聚合 | 记录结构化字段(决策类型、群体、申诉理由分类、处理时长、是否推翻),定期统计推翻率按群体的分布 | 个案无法证明系统性偏差;推翻率按群体分布本身就是极强的偏差探测器 |
| (b) 为什么不能直接拿未经检验的归因图。 ① 归因默认是近似,其忠实性必须用扰动 / 单调性 / 消融独立验证,否则「解释」可能与该样本的真实决策依据无关;② 单样本解释没有统计意义,也推广不到用户的整类内容;③ 更危险的是自动化偏置——人对流畅、可视的解释有系统性过度信任,一个不忠实的解释会让人更容易被误导,比自己判断还糟(方法 7.3.4 的使用纪律 2)。可用替代:给出与判定一致的规则类别 可核对的判据,并把「解释读起来顺」与「解释是对的」分开对待。 | ||
| (c) 指标与失败判据。 指标:通道可达性(通知到申诉的转化率、入口曝光率)、申诉量与处理量、推翻率(总体与按群体)、处理时长(中位数与 P95)、申诉原因分类分布。反直觉指标:申诉通道「太安静」通常不是好消息——若申诉量远低于预期,应先怀疑通道不可达、用户不知道它的存在、或答复成本高到没人愿意走完流程,而不是先庆祝「没有投诉」;因此要把可达性与处理量一起监控,两者都低时先查通道。失败判据:① 某群体推翻率相对整体显著抬升(例如 )→ 触发对模型的重新评估与分群复盘,而不是逐个改判;② 处理时长 P95 超过承诺时限(如 个自然日)→ 判为通道失效,先修产能与流程;③ 申诉量与拦截量之比低于预设下限而入口曝光正常 → 判为「用户在放弃」,需访谈并改进入口文案。 | ||
| (d) 时限与升级路径:首次答复 小时;判定答复 个自然日;涉及不可逆后果(封号、永久删除、收益扣除)的申诉进优先队列并双人复核;二次申诉走独立复核人且不得由同一人复核;所有时限写进文档并纳入看板;升级路径必须写明「谁有权覆盖模型输出」的具名角色,而不是「相关团队」。 | ||
| 评分要点:四要件齐备且「可推翻」写出两种权限 4 分;「可解释」写明忠实性未检验的风险与自动化偏置 2 分;反直觉指标 2 分;时限与升级路径可执行、含优先队列与独立复核 2 分。 |