引言:为什么形式化验证对AI基础设施至关重要
随着AI模型被部署到自动驾驶、医疗诊断和关键基础设施管理等安全敏感领域,仅依靠传统的测试集准确率已远远不够。我们必须能够证明模型在面对预期外的输入或对抗性扰动时,仍然能够保持预期的安全属性(例如鲁棒性、无偏差或稳定性)。
形式化验证(Formal Verification, FV)提供了一种数学上严谨的方法来证明这些属性。它不是测试输入空间的几个样本,而是试图证明模型在整个输入区域内都满足某个规范。
本文将聚焦于最关键的属性之一:局部鲁棒性(Local Robustness),并介绍一种高效且可操作的验证技术——区间界限传播(Interval Bound Propagation, IBP)。
核心技术:区间界限传播 (IBP)
局部鲁棒性定义为:对于一个输入 $x$,如果任何扰动后的输入 $x’$ 满足 $||x’ – x||_{\infty} \le \epsilon$,则模型在 $x’$ 上的预测结果应与在 $x$ 上的预测结果保持一致。
IBP 是一种抽象解释(Abstract Interpretation)方法。它不跟踪单个数值,而是跟踪每层神经元激活值的上界 $(U)$ 和下界 $(L)$。通过将输入扰动范围 $[[x-\epsilon, x+\epsilon]]$ 作为一个区间输入,IBP 可以计算模型输出层的激活值范围。如果在这个输出区间内,目标类别的激活值始终高于所有其他类别的最大激活值,则鲁棒性得到证明。
1. 线性层(Affine Layer)的传播
对于线性层 $y = Wx + b$,如果输入区间是 $[L_{in}, U_{in}]$,则输出区间 $[L_{out}, U_{out}]$ 可以直接计算得到:
$$L_{out}(i) = \sum_{j} (W_{ij}^+ L_{in}(j) + W_{ij}^- U_{in}(j)) + b_i$$
$$U_{out}(i) = \sum_{j} (W_{ij}^+ U_{in}(j) + W_{ij}^- L_{in}(j)) + b_i$$
其中 $W^+$ 和 $W^-$ 分别是权重矩阵 $W$ 中所有正值和负值的部分。
2. ReLU 激活函数的传播
ReLU 函数 $z = \max(0, y)$ 是非线性的,但 IBP 通过以下方式处理其区间 $[L_y, U_y]$:
- 全激活 ($L_y \ge 0$): $L_z = L_y, U_z = U_y$ (恒等映射)。
- 全失活 ($U_y \le 0$): $L_z = 0, U_z = 0$ (输出为0)。
- 交叉激活 ($L_y < 0$ 且 $U_y > 0$): $L_z = 0, U_z = U_y$ (最保守的界限)。
实操:基于 PyTorch 实现 IBP 验证器
我们将演示如何构建一个简化的 IBP 验证器来检查一个小型 MNIST 分类模型的局部鲁棒性。
步骤一:定义模型和验证函数
首先,我们定义一个简单的包含线性层和 ReLU 的模型。
import torch
import torch.nn as nn
# 假设我们有一个预训练好的简单模型
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = nn.Linear(784, 100)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(100, 10)
def forward(self, x):
x = x.view(-1, 784)
x = self.relu(self.fc1(x))
x = self.fc2(x)
return x
model = SimpleModel()
# 假设权重已加载
# 定义 IBP 传播函数
def ibp_propagate(layer, lower_bound, upper_bound):
if isinstance(layer, nn.Linear):
W = layer.weight
b = layer.bias
# 线性层传播公式 (W+ * L + W- * U) + b
W_pos = torch.clamp(W, min=0)
W_neg = torch.clamp(W, max=0)
new_lower = torch.matmul(lower_bound, W_pos.T) + torch.matmul(upper_bound, W_neg.T) + b
new_upper = torch.matmul(upper_bound, W_pos.T) + torch.matmul(lower_bound, W_neg.T) + b
return new_lower, new_upper
elif isinstance(layer, nn.ReLU):
# ReLU 传播公式
new_lower = torch.clamp(lower_bound, min=0.0)
new_upper = torch.clamp(upper_bound, min=0.0) # 因为 ReLU(x) <= max(0, x)
return new_lower, new_upper
elif isinstance(layer, nn.Flatten) or isinstance(layer, nn.Dropout):
# 忽略或透传
return lower_bound, upper_bound
else:
raise NotImplementedError(f"Layer type {type(layer)} not supported in IBP")
步骤二:执行验证
我们选择一个输入样本,定义一个 L-infinity 扰动 $\epsilon$,并进行验证。
# 假设 x_orig 是一个 1x784 的输入向量,且模型对其的预测是类别 5
x_orig = torch.rand(1, 784)
epsilon = 0.01 # L_inf 扰动半径
# 1. 定义初始区间
L_0 = torch.clamp(x_orig - epsilon, 0.0, 1.0) # 假设输入归一化在 [0, 1]
U_0 = torch.clamp(x_orig + epsilon, 0.0, 1.0)
# 2. 逐层传播
L, U = L_0, U_0
for layer in model.children():
L, U = ibp_propagate(layer, L, U)
# 3. 结果分析 (验证目标:原类别 C=5 必须是最大的)
target_class = 5
num_classes = L.shape[-1]
is_robust = True
# 检查每个非目标类别 i
for i in range(num_classes):
if i == target_class:
continue
# 鲁棒性证明条件:目标类别下界 > 非目标类别上界
# L[target_class] > U[i]
if L[0, target_class] <= U[0, i]:
# 找到了一个非目标类别 i,其最大可能激活值 U[i] 大于或等于目标类别的最小激活值 L[target_class]
print(f"[验证失败] 目标类 {target_class} 的下界 {L[0, target_class]:.4f} <= 非目标类 {i} 的上界 {U[0, i]:.4f}")
is_robust = False
break
if is_robust:
print(f"[验证成功] 模型在 L_inf={epsilon} 扰动下对类别 {target_class} 具有局部鲁棒性。")
IBP 的优势、局限与部署考量
优势
- 高效性: IBP 几乎完全依赖矩阵运算,相比基于 SMT 求解器(如 Reluplex 或 Marabou)的精确验证方法,速度极快,可以应用于大型模型。
- 可扩展性: 易于在 GPU 上并行化。
局限性
IBP 的主要局限在于其保守性 (Over-approximation)。由于 ReLU 交叉激活区域的处理方式(总是取 $L_z=0, U_z=U_y$),它引入了宽松的界限。这意味着 IBP 可能会报告模型不鲁棒(验证失败),但实际上模型是鲁棒的(假阴性)。
部署考量
在安全关键的 AI 部署管道中,可以将 IBP 作为部署前的“安全检查点”。对于验证成功的模型,我们可以自信地部署它们;对于验证失败的模型,我们需要使用更精确但计算成本更高的验证器(如 LP/SMT 求解器)进行二次检查,或者直接拒绝该模型,要求进行鲁棒性重训练(如对抗训练)。
通过将 IBP 集成到 CI/CD 流程中,我们可以确保任何模型更新都不会意外地引入安全漏洞,从而保障AI基础设施的可靠性。
汤不热吧