3.10 应用导读:Lab 2 人脸检测系统
前九节讲的是零件与原理,这一节把它们装成一台能跑的机器:Lab 2 要求你搭一条检测数据管线、训练一个能在一张图里定位并识别人脸的模型,再用对抗样本检验它的鲁棒性,并讨论隐私与偏见。
选人脸作为载体有三重理由:① 人脸是刚性、常见、尺度变化大的目标,正好暴露检测任务的核心困难(多尺度、类不平衡、定位精度);② 人脸携带生物特征,让 3.9 节的伦理议题不是抽象讨论而是可实验的对象;③ 人脸检测的经典方法(滑动窗口 + 级联分类器)与深度学习方法(单阶段密集预测)之间的演进,几乎就是本讲的浓缩版。
| 本讲原理 | Lab 2 中的对应实现 | 出处 |
|---|---|---|
| 卷积的局部性与权值共享 | 小 CNN 分类器(输入 窗口) | 3.1 |
| 感受野与步幅 | 窗口大小 = 网络感受野;滑动步幅 = 检测密度 | 3.2.5 |
| 输出尺寸公式 | 窗口裁剪与多尺度图像金字塔 | 3.2.2 |
| 池化与下采样 | 分类器主干 | 3.3.1 |
| 两宗罪 / 归纳偏置 | 为什么分类器只能用窗口而不是整图 | 3.1 |
| BN 与 Dropout | 训练稳定性与过拟合控制 | 3.3.3、3.3.4 |
| 迁移学习 | 用预训练骨干替换从零训练 | 3.6 |
| 检测 = 定位 + 分类 | 滑动窗口检测的全部内容 | 3.7.1 |
| IoU / NMS / mAP | 匹配正负样本、去重、评估 | 3.7.2 |
| 对抗样本 | 对抗扰动实验 | 3.9.3 |
| 偏见与隐私 | 分群评估与数据治理讨论 | 3.9.1、3.9.4 |
3.10.1 检测器的基本结构:滑动窗口
用一个固定尺寸 的窗口在图像上以步幅 滑动(通常还在多个尺度上重复),对每个窗口用一个小型 CNN 分类器输出「窗口内是否含目标」的分数。最终把高分窗口通过 NMS 合并为检测结果。
设图像尺寸为 、窗口 、步幅 ,单尺度下的窗口数为
它慢得可以量化: 图像、 窗口、步幅 4,单尺度 个窗口;若用 5 个尺度,就是 次独立前向。关键在于相邻窗口的输入几乎完全重叠——这些计算是重复的。
但它值得先理解,因为它把检测的本质摊开了:检测 = 在位置与尺度上搜索「分类器认为这里有目标吗」。理解了它,再看「共享卷积特征」的做法(现代检测器)就知道那只是把搜索共享了——卷积本身已经在所有位置滑动(3.2.8 的 im2col 视角),何必再手动循环。
Lab 2 的核心体验之一正是这个"慢":当你亲手跑完一轮推理、发现它需要几十秒时,「为什么要共享特征 / 要单阶段」这个动机就不需要别人解释了。
某实验用 输入、 窗口、步幅 4、共 4 个尺度(缩放因子 )。
(a)单尺度的窗口数是多少?(b)四个尺度总计多少次前向?若每张图用 3 个尺度,总数是多少?(c)设单次前向(批大小 1)耗时 ms,全部窗口逐次前向的总耗时是多少?如果改成批大小 64 一次前向,能否减少总时间?为什么?
(a) 单尺度
(b) 尺度 时 个窗口。缩放后图像尺寸变小,窗口数随之减少:
| 缩放因子 | 图像边长 | 窗口数 |
|---|---|---|
| 256 | ||
| 205 | ||
| 154 | ||
| 102 |
四个尺度合计 次前向;只用 3 个尺度()则为 次。
(c) 逐次前向:(3 个尺度时 )。
批处理能显著加速(因为单次前向在 GPU 上远未打满算力,瓶颈是 kernel 启动开销与访存),但不能消除根本问题:总计算量(FLOPs)与窗口数成正比。批处理只是把"每个窗口单独一次前向"的低效率摊销掉,没有减少重复计算——相邻窗口仍然各自完整地卷了一遍自己那一小块。
这正是「共享特征」要解决的:如果整图先过一次卷积得到特征图,那么在特征图的每个位置上"顺便"评估一个窗口,成本就与研究区域的规模同量级,而不是与窗口数量 × 窗口面积同量级。数量级差异就在这里。
∎3.10.2 数据管线:从图像与标注到训练样本
1. 读入与归一化
- 读取图像 + 一组框(标注常为 (x1, y1, x2, y2) 绝对坐标)
- 像素缩放到 [0,1] 或按通道做 z-score(与骨干预训练一致,[3.3.5](sec33.md#sec-3.3.5))
- 检查越界框:坐标必须裁剪到图像范围内,否则裁剪会引入形状错误的 patch
2. 坐标表示的选择与转换(最容易出 bug 的一步)
- 角点式 (x1,y1,x2,y2):便于裁剪与 IoU 计算,但宽高可能为非法负值
- 中心式 (cx,cy,w,h):便于回归(宽度变化时中心稳定),但需从角点式转换
- 转换公式:
cx = (x1+x2)/2, cy = (y1+y2)/2, w = x2-x1, h = y2-y1
x1 = cx - w/2, y1 = cy - h/2, x2 = cx + w/2, y2 = cy + h/2
- 断言检查:转换后再转回去,数值应完全相等(浮点误差 < 1e-6)
3. 多尺度 / 多位置采样:把"图 + 框"变成"窗口 + 标签"
- 对每个窗口计算它与所有真值框的 IoU
- IoU ≥ 正阈值(如 0.5)→ 正样本(含目标);IoU < 负阈值(如 0.3)→ 负样本(背景)
- 处于中间区间(0.3–0.5)的样本通常被丢弃("忽略区"),避免给模型错误的监督信号
4. 类别不平衡处理(检测任务的核心难点)
- 背景窗口远多于目标窗口(可到 100:1 以上)
- 做法:随机负样本采样(只取一部分负样本)、难例挖掘(hard negative mining:
优先保留被模型误判为正的负样本)、或用 focal loss 让难样本主导梯度
5. 数据增强(对检测要同时变换图像与框!)
- 几何:水平翻转(框的 x 坐标镜像)、随机裁剪 / 缩放、随机平移
- 光度:亮度 / 对比度 / 色彩抖动
- 常见 bug:只增强图像不增强框 → 标签错位,模型学到"随机噪声"
- 断言检查:在增强后的图上画出框并可视化检查(这一步几乎总能发现管线 bug)
一个窗口与真值框的 IoU 是 时,它到底算不算"含目标"?
- 若判为正样本:模型被要求在一个明显偏移的窗口上说"有人脸",这会给定位分支注入错误监督,学习到的边界会变糊。
- 若判为负样本:模型被要求在一个确实覆盖了目标大部分的窗口上说"没有",同样错误。
两种标注都不对,所以标准做法是把它从训练中排除(在损失上给 0 权重)。这不是偷懒,而是承认"这个样本的标签本身有歧义"——在标注有噪声的场景里,承认不确定性比强行二值化更安全。这与 3.9 节的原则一致:模型能表达的东西受监督信号质量限制。
代价:忽略区浪费了一部分样本。因此有些工作用软标签(按 IoU 连续加权)来利用这些样本,而不是直接丢弃。
图像中有一个真值人脸框 (边长为 )。现在有四个候选窗口:
设正阈值 、负阈值 。分类每个候选窗口(正 / 负 / 忽略),并指出哪个候选最适合用来训练定位(框回归)分支。
面积: 的面积为 。
窗口 A :宽高均 ,面积 。 交:、 → (A 完全落在 G 内)。 并 。 → 正样本
窗口 B :宽高均 ,面积 。 交:、 → 。 并 。 → 负样本
窗口 C :与 只在角点相接,交集面积 (按坐标差计算,边界不算入)→ → 负样本
窗口 D :宽 、高 ,面积 。 交:(宽 )、(高 )→ 。 并 。 → 正样本
结论与选择:
| 窗口 | IoU | 分类 |
|---|---|---|
| A | 0.81 | 正样本 |
| B | 0.16 | 负样本 |
| C | 0.00 | 负样本 |
| D | 0.76 | 正样本 |
用于训练定位(框回归)分支的最佳候选是 A:A 与真值形状完全一致(均为 ),只是位置有 像素偏移——这是一个"干净"的回归目标(模型要学的正是"往左上/右下各挪一点"这种小修正)。相比之下 D 的宽高与真值不同( 对 ),回归目标同时包含尺度与长宽比的修正,虽然 IoU 也够高,但它的"偏移分布"更宽、更难学。
一条实用的工程经验:正样本的 IoU 排行经常被用作损失的加权依据(IoU 越高的样本对回归贡献越大),或者用"质量感知"的损失(如 IoU-aware 的分数预测)来避免"高分低质量框"——即在训练时就把"置信度"与"定位质量"绑定起来,而不是事后靠 NMS 挑。
∎import torch, torch.nn.functional as F
# 滑动窗口推理的最小骨架:批量化 + 坐标还原(Lab 2 的核心循环)
def sliding_window_detect(model, img, win=32, stride=4, scale=1.0, batch=256, thresh=0.5):
"""返回原图坐标系下的 (x1, y1, x2, y2, score) 列表。"""
model.eval() # 别忘了!BN 统计量问题见 3.3.3
H0, W0 = img.shape[-2:]
if scale != 1.0: # 多尺度:先缩放图像
img_s = F.interpolate(img[None], scale_factor=scale, mode="bilinear",
align_corners=False)[0]
else:
img_s = img
H, W = img_s.shape[-2:]
wins, coords = [], [] # 窗口张量 + 其在**缩放图**中的左上角
for y in range(0, H - win + 1, stride):
for x in range(0, W - win + 1, stride):
wins.append(img_s[:, y:y + win, x:x + win])
coords.append((x, y))
if not wins:
return []
scores, boxes = [], []
for i in range(0, len(wins), batch): # 批量化:把上万次前向压成几十次
chunk = torch.stack(wins[i:i + batch]) # (B, C, win, win)
with torch.no_grad(): # 推理不要建图,省显存
p = torch.sigmoid(model(chunk)).squeeze(1)
scores.append(p.cpu()); boxes.append(torch.tensor(coords[i:i + batch]))
scores, boxes = torch.cat(scores), torch.cat(boxes)
keep = scores > thresh
out = []
for (x, y), p in zip(boxes[keep].tolist(), scores[keep].tolist()):
# 关键:把**缩放图**坐标除以 scale 还原到**原图**坐标(3.10.5 的第 5 条 bug)
x1, y1 = x / scale, y / scale
x2, y2 = (x + win) / scale, (y + win) / scale
out.append((x1, y1, min(x2, W0), min(y2, H0), p))
return nms(out, iou_thr=0.5) # 去重(见下)
def iou(a, b):
"""连续坐标约定:面积用 x2 - x1(不加 1)。越界请先裁剪到图像范围内。"""
ix1, iy1 = max(a[0], b[0]), max(a[1], b[1])
ix2, iy2 = min(a[2], b[2]), min(a[3], b[3])
iw, ih = max(0.0, ix2 - ix1), max(0.0, iy2 - iy1) # 不相交时为 0,不要取负
inter = iw * ih
union = (a[2]-a[0])*(a[3]-a[1]) + (b[2]-b[0])*(b[3]-b[1]) - inter
return inter / union if union > 0 else 0.0
def nms(dets, iou_thr=0.5):
"""dets: [(x1,y1,x2,y2,score)]。注意比较方向:IoU **大于**阈值才抑制。"""
dets = sorted(dets, key=lambda d: -d[4]) # 按分数从高到低
kept = []
while dets:
best = dets.pop(0); kept.append(best)
dets = [d for d in dets if iou(best, d) <= iou_thr] # 方向写反 = 删掉所有框
return kept
model.eval():BN / Dropout 在训练与推理模式下行为不同(3.3.3、3.3.4)。漏掉它,单张图推理会得到与训练时完全不同的结果。x / scale:多尺度推理时,模型看到的是缩放后的图像,坐标必须先还原。漏掉它,多尺度的框会全部错位(且不会报错)——这是最隐蔽的一类 bug。iou(best, d) <= iou_thr:NMS 的方向。写反的后果是「所有框都被删掉,只剩下第一个」,表现为"检测器只输出一个框"。
为什么要把这三条写成断言:检测管线的错误大多不会抛异常——形状对、类型对、数值在合法范围内,只是语义错了。这类错误只能靠显式的断言与可视化检查抓出来。在训练循环里补三行断言(坐标范围、IoU 在 、标签分布),通常比多训几个 epoch 更有价值。
3.10.3 训练:损失、稳定性与评估
模型
- 小型 CNN 分类器(若干卷积 + BN + ReLU + 池化 + 全局池化 + 线性头)
- 若允许使用预训练骨干([3.6](sec36.md)):优先用,收敛快且更稳
损失
- 分类:二元交叉熵(是否为目标)
- 若做框回归:分类损失 + λ · 回归损失(常为平滑 L1 / IoU 系损失)
- 类别不平衡:正负样本加权,或 focal loss
优化
- AdamW,学习率 1e-3 量级;batch 大小按显存取
- 先用较小的学习率跑通 1 个 epoch,确认 loss 下降再放大
- 早停:以验证集指标(precision / recall / F1)为准,不看训练 loss 单值
训练稳定性自查(按顺序排查)
1. 第一步 loss 是否 ≈ -log(正样本比例)(全判背景的基线)?不是 → 数据管线有 bug
2. 有没有断言检查张量形状与取值范围?没有 → 先加
3. 可视化 32 个训练样本(图 + 标签)→ 标签错位一眼可见
4. 过拟合 10 个样本能否达到接近 100% 的准确率?不能 → 模型或损失有问题(健壮性测试)
5. 再谈调参
评估
- 不看准确率([3.7.2](sec37.md#sec-3.7.2) 的教训):用 precision / recall / F1,
或按 IoU 阈值计算 AP
- 报告"漏检"与"误检"分开计数,不要只报一个综合分
- 多尺度推理时报告不同尺度的表现(小目标通常最差)
- 固定随机种子,报告多次运行的均值与方差(小数据下随机性极大)
这个做法的价值在于把"能不能学"与"数据够不够"这两件事分开。
如果模型连 10 个样本都拟合不了,那问题一定在实现层面(形状错、损失方向错、标签错、梯度没回传),而不是"数据量不足"或"模型不够大"。反过来,如果 10 个样本能完美拟合但验证集表现差,问题才在泛化(第 1 讲 1.7 节)。先做这个测试,可以避免在错误的方向上调几天参数。
对检测任务,这个测试还有一个附加用途:确认框回归的分支确实在学(可以打印预测框与真值框的坐标,观察它们是否在靠近)。只分类不回归,或回归目标被写错符号,都是常见实现错误。
- 框的坐标顺序错(把 当成 ):不会报错,只会让所有标签错位,表现为"训练 loss 能降但检测框乱飞"。用断言验证 round-trip 转换。
- 增强只改图像不改框:这是检测特有的 bug(分类任务不会有)。在增强后的图上可视化框是最快的检查方式。
- IoU 计算把边界算进了面积:应该用 而不是 (连续坐标约定),否则 IoU 会有几个百分点的偏差,在阈值附近改变正负样本划分。
- NMS 的阈值方向搞反:抑制条件是「与最高分框的 IoU 大于阈值」(而不是小于);写反会把所有不重叠的框都删掉。
- 多尺度推理时忘记把坐标映射回原图:每个尺度上得到的框坐标是缩放后图像的坐标,必须除以缩放因子才能还原。忘了这一步,多尺度的结果会完全错位。
- 忘了
model.eval():BN 仍按当前批统计(3.3.3),单张图推理时统计量退化,结果与训练时完全不同。这是最容易被忽略的一行。
3.10.4 对抗样本实验:把 3.9.3 节落到代码
目标:让分类器把"含人脸"的窗口判成"不含"(或反过来),并量化扰动幅度。
1. 取一张测试图的一个正样本窗口 x,标签 y=1
2. 打开梯度:requires_grad(注意:此时不是训练,但需要梯度)
3. 计算损失 L = BCE(f(x), y)
4. 求 ∂L/∂x(对**输入**求导,而不是对参数)
5. FGSM:x' = clamp(x + ε·sign(∂L/∂x), 0, 1)
注意 clamp 到合法像素范围——否则会得到"物理上不存在"的图像
6. 用 x' 前向:观察输出的置信度从 0.x 掉到多少
7. 记录指标:
- 攻击成功率(多少窗口被成功翻转到错误标签)
- 扰动幅度:‖δ‖_∞ = max|x'-x| 与平均绝对扰动
- 人眼可见性:把 x'-x 放大显示(对比原图)
8. 进阶:PGD(多步迭代 + 每步投影回 ε 球内),比较它比 FGSM 强多少
9. 迁移测试:在模型 A 上生成的扰动,拿去攻击模型 B(结构不同的同类分类器)
—— 若成功率仍显著,说明这是"模型无关"的弱点,而不是 A 的特有 bug
必须同时报告的三件事,缺一不可:① 扰动幅度( 与平均幅度);② 攻击前后的置信度变化;③ 扰动可视化(把 放大若干倍显示,让读者自己判断"是否可见")。
因为它会让三件抽象的事变成可观察的事实:
- "不可察觉"是可以被检验的。当你把 的噪声放大 32 倍显示时,你会亲眼看到它是有结构的、类似纹理的图案,而不是随机噪声——因为它是沿梯度方向的、针对该模型的特定模式。这和"加高斯白噪声"完全不同。
- 模型的行为跃变是突然的。置信度可能从 直接掉到 ,而不是平滑下降——这正是"决策边界离数据点很近"的可视化证据。
- 防御不是显然可行的。你可以立刻尝试 3.9.3 里的简单防御(JPEG 压缩、去噪、随机缩放),然后发现:在放大攻击(自适应)下它们常常失效。
最后这一点是本实验最重要的输出:它让人从"我知道有对抗样本"变成"我理解为什么鲁棒性这么难"。这与第 1 讲"把反直觉的结论亲手验算一遍"的教学逻辑一致。
3.10.5 常见失败模式与排查表
| 症状 | 最可能的原因 | 检查方法 |
|---|---|---|
| 训练 loss 不下降 | 学习率过大 / 标签与预测顺序错 / 梯度未回传 | 先过拟合 10 个样本;打印 loss 与输入标签 |
| loss 下降但检测框乱飞 | 框坐标顺序或表示弄错(角点 vs 中心) | round-trip 断言;可视化预测框与真值框的叠加图 |
| 检测框大量重复 | NMS 没做 / 阈值过大 / NMS 条件写反 | 打印 NMS 前后框数;检查 IoU 与阈值比较方向 |
| 大量漏检 | 正负阈值设得太严 / 负样本采样过多 / 尺度覆盖不全 | 统计训练集中正样本数量;分别报告各尺度的召回率 |
| 大量误检 | 负样本不足(尤其缺"难负样本")/ 阈值过低 | 查看误检样本内容(是否都是纹理强烈的背景?) |
| 小目标检测不到 | 感受野 / 尺度不匹配;多尺度没覆盖 | 按目标尺寸分组统计召回率 |
| 验证指标远低于训练 | 数据泄漏(同一人的多张照片跨划分)或过拟合 | 检查划分是否按"人"而非按"图"划分 |
| 单张推理结果异常 | 忘了 model.eval()(BN 统计量问题) |
切换 eval 后再测 |
| 多尺度结果错位 | 忘记把坐标映射回原图 | 打印各尺度输出的坐标范围并与图像尺寸对照 |
| 结果不可复现 | 随机种子 / 数据顺序 / 增强随机性未固定 | 固定种子,记录版本与超参(工具页) |
Lab 2 完成后,你会得到一个能框人脸的模型。但把它变成"系统"还差几件东西——这些恰恰是本讲后面几节讨论的:
- 分群评估(3.9.1):Lab 的人脸数据能按什么维度分群?如果分不出来,说明数据本身缺乏代表性,这本身就是一个需要记录的事实(也对应 3.10.6 的实验设计)。
- 不确定时拒答:给置信度设下限,低于它就不输出框。这比"输出所有框再让人挑"更诚实。
- 隐私约束(3.9.4):原始人脸图像是否需要保留?能否只留特征模板?EXIF 是否被清理?
- 性能与延迟预算:滑动窗口的
12,005次前向在真实场景不可接受;真实的替换路径是:共享特征 → 单阶段检测 → 骨干替换(3.7.3)。 - 失败模式的可观测性:记录被拒样本、低置信样本、以及"框特别大 / 特别小"的异常输出——真实系统的改进往往来自对这些日志的分析,而不是重新训练。
一句话:Lab 2 教的不是"人脸检测",而是"把一个视觉任务从原理做到能跑、再到能负责"的完整流程。后面缺的那几件东西,正是把"能跑"变成"能负责"的部分。
3.10.6 把伦理讨论落到代码:三个可做的实验
实验 A · 分群评估
1. 把验证集按"光照条件 / 性别 / 是否戴眼镜 / 图像来源"分组(能分出什么就用什么)
2. 对每个子群分别计算 precision / recall / F1
3. 画出各组指标对比图,并标注每组的样本量
4. 关键:样本量过小的组要标注"结论不可靠",不要把它当成发现
→ 对应 [3.9.1](sec39.md#sec-3.9.1)
实验 B · 阈值与代价
1. 画出调节置信度阈值时的 precision-recall 曲线
2. 为两个不同场景各选一个工作点:
(i) 门禁(误识代价高)→ 高阈值、高 precision;
(ii) 相册自动整理(漏检代价高)→ 低阈值、高 recall
3. 说明"同一个模型在两个场景应该用不同阈值"——阈值是业务决策,不是模型属性
→ 对应 [3.9.2](sec39.md#sec-3.9.2)
实验 C · 对抗与隐私
1. FGSM 攻击([3.10.4](#sec-3.10.4))
2. 隐私检查:读取原始图像的 EXIF,列出其中可识别个人信息(GPS、设备、时间)
3. 讨论题(写进报告):这段代码如果部署到公共场所,应当增加哪些默认约束?
→ 对应 [3.9.3](sec39.md#sec-3.9.3) 与 [3.9.4](sec39.md#sec-3.9.4)
报告要求(这三条比结果本身更重要):
- 报告"没做什么":没调参、没做数据清洗、样本量不足的部分都要明说——避免把"未评估"包装成"没问题"。
- 区分"观测"与"解释":例如"子群 B 的召回率低 个百分点"是观测;"因为该子群样本少"是解释——解释需要额外证据(如样本量统计、混淆因素排查)。
- 给出失败判据:明确说明"在什么条件下这个模型不应该被使用"。这与 3.9.5 的出口判据一致——一个负责任的实验报告应该包含使用边界的陈述。
Q1 图像、 窗口、步幅 8,单尺度有多少个窗口?若用 3 个尺度(),总计多少次前向?
Q2 真值框 ,候选窗口 与 ,分别求 IoU 并判断是否为正样本(正阈值 )。
Q3 为什么检测任务需要「忽略区」(IoU 在正负阈值之间的样本)?不用会怎样?
Q4 你训练时 loss 正常下降,但推理时每个窗口的分数几乎都相同、框的位置也几乎不动。列出最先应该检查的两件事。
Q5 用 FGSM 攻击自己的检测器时,为什么要 clamp(x', 0, 1)?不 clamp 会得到什么?
参考要点
Q1 单尺度 个窗口。3 个尺度: 时 ; 时边长为 ,; 时边长为 ,。合计 次前向。
Q2 候选 1:宽高均 ,面积 ;交 (完全在真值内);并 ; → 正样本。候选 2:宽高均 、面积 ;交 ;并 ; → 正样本(刚过阈值)。注意两者都为正,但候选 1 更"干净"(形状与真值一致、只是位置偏移)。
Q3 中间 IoU 的窗口语义上有歧义:判正会教模型在被平移的窗口上说"有人脸"(边界变糊),判负会教它在覆盖了目标大部分的窗口上说"没有"(同样错误)。所以标准做法是用权重 0 排除它们。不设忽略区,模型会收到自相矛盾的监督,表现为置信度校准差(同类目标分数高低与 IoU 不相关)、边界不稳。
Q4 优先检查:① 忘了 model.eval()(BN 在批大小 1 时统计退化,3.3.3);② 框的坐标表示或标签错位(例如把中心式当角点式用),用 round-trip 断言与可视化检查;其次才考虑学习率 / 数据不足。
Q5 clamp 保证生成的仍然是合法的图像张量(像素在 之间)。不 clamp 会得到超出像素范围的"图像"——它在数值上可以让损失降得更低(离决策边界更远),但不能被真正显示或馈入后续的图像预处理,实验结论也就失去了"人眼不可察觉"这一前提。
- MIT 6.S191 的 Lab 2(人脸检测与对抗样本,仓库
github.com/aamini/introtodeeplearning)——本 Lab 的母本任务;引用时注明所对照的版本与公开材料。 - 本教程 Lab 2 · 人脸检测系统——任务书与验收标准(本节是其理论导读)。
- 对抗样本:3.9.3 的延伸阅读(FGSM arXiv:1412.6572、PGD arXiv:1706.07334)。
- 公平性评估:Buolamwini & Gebru, Gender Shades(2018,FAT*);数据与模型的文档化:Datasheets for Datasets(arXiv:1803.09010)、Model Cards for Model Reporting(arXiv:1810.03993)。
- 检测器评估口径:3.7.2(IoU / NMS / AP 与 COCO 口径)。
- 工程调试:工程工具箱(种子、日志、版本管理、显存估算)。
本节小结:Lab 2 把本讲的零件装成一台机器——滑动窗口把分类器变成检测器(并暴露出“重复计算“这个动机,例 3.10.1);数据管线的三个关键点是框的坐标表示与 round-trip 断言、IoU 阈值与忽略区、以及图像与框必须同步增强(例 3.10.2);训练上「先过拟合 10 个样本」是最有效的调试手段;对抗样本实验的价值不在于复现攻击,而在于亲手验证「不可察觉」「行为跃变」「防御不易」这三件事;最后,把伦理讨论落到分群评估、阈值与业务代价、隐私检查这三个可执行的小实验上。做完这一步,你就把第 3 讲从“读懂了“推进到了“能负责“。