
AI模型在实验室里测出来的准确率再高碰上真实世界还是可能翻车——一个被贴纸干扰的视觉识别系统就能把“停止”标志认成“限速”。这时候工程师常问一句话除了多跑测试还有没有办法“证明”某个行为永远不会出错有答案就是形式化方法Formal Methods。这门技术已经存在几十年核心是用数学逻辑描述系统行为再做自动化验证。而它之所以现在被频繁和人工智能Artificial Intelligence放在一起讨论是因为我们开始需要给深度学习这类黑盒系统提供数学级别的安全保证。这篇文章我想用最直白的方式拆解这个交叉领域先讲清楚为什么AI安全验证需要形式化方法再梳理两条技术路线——用形式化方法验证AI以及用AI增强形式化方法。最后会给一个用Z3验证神经网络鲁棒性的可运行实例并分享我在实际验证中踩过的坑。内容偏入门到中级适合正在做AI安全方向研究、搞相关毕业设计或者对“可证明正确的人工智能”感兴趣的工程师和同学。1. AI系统最缺的是数学意义上的“没问题”1.1 测试为什么永远填不平安全缺口深度学习模型本质上是拟合一个高维函数。在高维空间里哪怕训练集和测试集的分布已经很接近仍然可能存在无数个我们从未见过的输入点让模型输出突然改变。传统软件测试能做大量随机抽样但抽样测试天然存在盲区——它只能覆盖有限样本无法对无限输入空间做出断言。打个比方质检员抽检一百个零件都合格不代表一整批零件都合格而形式化验证相当于对“所有零件”做数学上的证明。当然前提是我们把“合格”的定义用严格的数学规格写下来。在自动驾驶、医疗影像、工业质检这类高风险场景AI系统一旦出错代价不是重新训练一次模型那么简单。我见过不少项目上线前跑了几万条测试用例全部通过结果在真实环境里遇到一个从没见过的光照条件就误判。这类问题靠“多测几组数据”很难根除因为输入空间实在太大了。所以业内逐渐形成一个共识对于关键决策不能只靠一个“准确率不低”的模型拍板还需要一层能给出数学证据的安全机制。1.2 形式化方法的老底子形式化方法不是新词。上世纪六七十年代逻辑学家和计算机科学家就在研究如何用数学严格描述和验证程序后来被用在航空航天软件、芯片设计、高铁信号系统等安全攸关领域。英特尔在芯片验证中用形式化方法抓过浮点除法bug欧洲航天局在火箭控制软件中也广泛应用验证工具。它最核心的四种技术我简单梳理一下形式化规格Formal Specification用逻辑、自动机、时序逻辑等精确语言把一个系统“应该做什么”写下来。常见的符号有线性时序逻辑LTL、计算树逻辑CTL。模型检测Model Checking通过遍历系统状态空间检查系统是否满足规格。如果违规存在会给出从初态到违背状态的完整路径。定理证明Theorem Proving在公理体系里用逻辑推理证明系统性质像Coq、Isabelle/HOL、Lean这些证明助手把证明过程写成严格可校验的步骤。抽象解释Abstract Interpretation在简化抽象域上对程序做保守近似分析经典的是区间分析把程序中每个变量限定在一个区间内从而快速排除一类错误。这里关键的一点是形式化方法给出的答案是“绝对性”的。模型检测返回UNSAT就说明在所有状态空间里都不可能发生该违规定理证明成功就说明该性质在公理体系内成立。这跟“做了多少测试”没有关系它看的是“所有可能性”。1.3 人工智能遇上形式化方法是必然当AI开始控制物理世界时对“数学证明”的需求就变得迫切。具身智能机器人、自动驾驶、边缘计算平台上的实时推理这些系统都在做高维感知和高风险决策。如果目标只是推荐视频、识别猫狗照片准确率已经足够但一旦涉及人身安全光有经验验证就不够了。同时AI本身是一种复杂的软件系统但它又打破了传统软件“有明确状态和路径”的假设——神经网络是连续函数、非线性激活、高维输入。传统形式化工具直接套不上去必须发展新方法。反过来形式化方法自身也面临状态爆炸的计算瓶颈需要在搜索策略上引入机器学习。两条路线的碰撞正是当前这个交叉研究领域最迷人的地方。2. 双线并行验证AI也用AI升级验证2.1 用形式化方法验证AI核心是“鲁棒性证明”深度学习的风险有很多隐私泄露、偏见、公平性、对抗扰动。其中对抗扰动和分布偏移是形式化方法最能直接发力的地方。形式化验证要回答的问题可以定义为给定模型 f(x) 和一个输入 x0验证在输入邻域 B(x0, ε) 内模型输出类别保持稳定。用集合语言写就是对任意 x ∈ B(x0, ε)都有 argmax f(x) argmax f(x0)。由于量化命题很难直接处理通常的做法是取反尝试寻找一个反例 x*使得 x* 在邻域内但 argmax f(x*) ≠ argmax f(x0)。如果不存在这样的 x*就说明模型在这个邻域内是鲁棒的。找反例的过程可以转化为约束求解问题。具体技术路线有几条约束求解SMT/MILP把网络结构和输入输出关系编码为数学约束交给Z3、Gurobi这类求解器。优点是精确缺点是可扩展性差。对真实规模的网络几乎做不了端到端精确验证。抽象解释把输入从一个点扩展成一个抽象域区间、zonotope、多面体等逐层传播得到一个输出边界。如果输出边界里没有别的类别就证明鲁棒。这是目前工程上最可行的一类方法。分支定界Branch and Bound在抽象解释的基础上把某些神经元的ReLU激活区域一分为二分别计算边界从而逐步收紧结果。著名工具α,β-CROWN就走这个路线。为什么这条路困难一句话这类问题在最坏情况下是NP-难的。神经元越多、维度越高搜索空间越爆炸同时ReLU一引入函数就从线性变成分段线性让分析复杂度直接上升。2.2 用AI增强形式化方法让搜索不再盲目反过来形式化方法也有自己的烦恼状态空间爆炸、搜索策略难选、分支变量不会挑。AI这时候可以帮忙。举几个典型场景模型检测里的状态扩展顺序传统BFS/DFS本质上没太多智能用机器学习预测“哪些状态更有可能通向反例”优先扩展它们可以大幅缩短找反例时间。SMT求解器里的分支策略求解器面对大量子句每次决策选哪个变量进行分支对速度影响极大。已经有工作用强化学习训练分支策略效果接近甚至超过人工设计的启发式。自动定理证明里的证明脚本生成在Lean、Isabelle等证明助手里使用图神经网络和强化学习选择引理、构造证明序列。DeepMind在自动数学推理上的很多工作都与此相关。循环不变式推断证明程序正确性时循环不变式是最难的部分。传统需要专家手工编写现在可以用机器学习从候选样本中自动学习候选式再由证明器确认。这里有一个很多初学者容易搞混的坑AI加速后的形式化验证结论还靠不靠得住答案仍然靠得住前提是AI只用于搜索启发最终的SAT/UNSAT结果还是由求解器严格推导。AI可能让求解器更快找到反例也可能让证明器的搜索效率大幅提高但它并不改写验证过程的逻辑语义。这个边界必须把握住不要把AI的“概率性”带到验证结论里。2.3 两条路线其实是一个闭环把两条线放在一起看会发现它们不是孤立的AI系统越复杂越需要更强的形式化验证验证工具越强大AI系统才敢于部署到更关键的领域。在实际项目中理想的形态是“可验证训练运行时验证传统测试”三者配合训练时把鲁棒性当作优化目标比如用可验证训练IBP、CROWN训练部署时在边缘侧放一个轻量级验证器/监控器同时保留随机测试和模糊测试做回归。为了直观比较我把两条路线的特征列个表路线核心问题主要难度代表性工具/方向产出结果验证AI证明神经网络在扰动区域内行为符合预期高维非线性、组合爆炸Z3、α,β-CROWN、ERAN反例或鲁棒性证书AI辅助验证降低模型检测/SMT/证明的搜索开销搜索策略设计、数据集构造RL分支策略、图神经网络证明搜索更快的SAT/UNSAT判定3. 实操用Z3证明一个两层神经网络的局部鲁棒性3.1 场景设定与验证目标我构造一个非常小的二分类网络来说明整个流程输入2维隐藏层3个神经元ReLU激活输出层2维。在原始样本 x0 (0.5, 0.3) 处模型会把类别0判为第一类即输出 y0 y1。我们要回答的问题是在L∞范数扰动 ε0.1 的范围内也就是 x0 每个维度最多变化0.1的矩形区域是否存在一个输入会让模型把类别判成 y1 ≥ y0如果存在这个输入就是对抗样本如果不存在就相当于证明了模型在这个局部区域不会翻转分类。为什么选L∞而不是L2因为L∞约束写起来最直观在Z3里就是简单的上下界不等式想换成L2也可以但需要额外处理平方和约束求解速度会慢不少初学不推荐。3.2 从神经网络到约束公式要把神经网络翻译成Z3能理解的逻辑约束核心工作是分层编码输入层定义两个实数型变量 x0、x1加上扰动约束。隐藏层每个神经元的输出等于激活函数的输出。ReLU本质是分段函数h max(0, Wx b)。在Z3里用 If 函数把它写成 If(线性值 0, 线性值, 0)。输出层也是一个线性变换直接把隐藏层输出乘权重加偏置。分类翻转条件要寻找反例就往求解器里加入“输出2 ≥ 输出1”的约束。这里有一个操作上的技巧先把网络在 x0 处的前向输出算一遍确认 y0 y1不然验证目标本身就不成立。这个可以用普通Python计算也可以在Z3里求解 x x0 时的 y 值。3.3 完整可运行代码from z3 import * # ---- 网络参数手工指定方便复现---- # 输入维度 2隐藏层 3输出维度 2 W1 [[1.2, -0.5, 0.8], [0.3, 1.1, -0.2]] b1 [0.1, -0.1, 0.2] W2 [[0.9, -0.4, 0.6], [-0.3, 0.8, -0.2]] b2 [0.05, -0.05] x0 (0.5, 0.3) # 原始样本 eps 0.1 # L_inf 扰动半径 # ---- 定义Z3变量 ---- x RealVector(x, 2) # 输入 h RealVector(h, 3) # 隐藏层输出 y RealVector(y, 2) # 输出层 s Solver() # 1. 输入扰动约束x 落在 x0 的 L_inf 邻域内 s.add(x[0] x0[0] - eps, x[0] x0[0] eps) s.add(x[1] x0[1] - eps, x[1] x0[1] eps) # 2. 隐藏层ReLU(W1x b1) for i in range(3): linear_val W1[0][i] * x[0] W1[1][i] * x[1] b1[i] s.add(h[i] If(linear_val 0, linear_val, 0)) # 3. 输出层y W2h b2 for j in range(2): s.add(y[j] W2[0][j] * h[0] W2[1][j] * h[1] W2[2][j] * h[2] b2[j]) # 4. 验证目标是否存在输入使得输出类别1y[1]不小于类别0y[0] # 注意原始样本应该是 y[0] y[1]如果这个约束满足则说明模型被翻转 s.add(y[1] y[0]) result s.check() if result sat: m s.model() print(发现反例对抗样本) print(x , [m.eval(x[i]) for i in range(2)]) print(y , [m.eval(y[i]) for i in range(2)]) print(扰动大小, [float(m.eval(AbsReal(x[i] - x0[i]).simplify())) for i in range(2)]) elif result unsat: print(证明完成在 L_inf , eps, 的范围内不存在能让分类翻转的输入) else: print(求解器无法判定)这段代码在 z3-solver 4.8 版本上可以直接跑。装上依赖后大概率你会得到UNSAT结果因为隐藏层只有三个神经元决策边界很粗糙ε0.1的小邻域内很难翻转。如果你想看到SAT反例把 ε 改成0.5大概率就能找到。3.4 输出解读、参数调整与局限说明跑通之后有几个点值得深挖。第一验证成功证明的是“局部鲁棒性”不是“全局鲁棒性”。它只覆盖以 x0 为中心、大小为 ε 的矩形区域。真实模型需要覆盖很多这样的区域才有意义。第二Z3用的是实数算术而PyTorch等框架推理用的是浮点数。所以验证结果在数学形式上成立但到了真实部署环境可能因为浮点舍入出现极端情况。工程上常采取“留一点安全边际”的做法把扰动边界略微放大或者给输出不等式加一个很小的偏置让证书稍微保守一点。第三这里的网络很小Z3几毫秒就能出结果。遇到真实规模网络直接把这个代码放大是跑不动的——你很快就会卡在求解速度上。正确的升级路径是先试ERAN、α,β-CROWN这类抽象解释/分支定界工具再考虑用AI辅助加速求解。这个渐进路线对新手特别友好。第四参数选择上ε 用什么值并没有标准答案它应该取决于输入特征的物理含义。如果输入是像素值0到1ε0.1代表10%的像素扰动其实已经不小如果输入是归一化的传感器数据ε0.001可能才算安全边界。一定要回到具体业务里去定义。4. 常见问题与排查技巧实录4.1 我遇到过的五种实际翻车现场直接上一份实测过的排查表都是我踩过的坑症状可能原因排查与解决Z3长时间无结果ReLU产生的析取约束组合爆炸调小ε减少神经元设置超时给求解器上限找到“反例”但实际模型不翻转实数假设与浮点推理存在数值差异在翻转条件里加margin比如 y[1] 0.001 y[0]抽象验证一直说“不鲁棒”抽象域过近似太强切换更高精度抽象域如zonotope、DeepPoly验证结论和PyTorch前向结果对不上维度或权重行列写错、ReLU边界写反先不加对抗约束验证 x0 处的输出一致性训练精度高但鲁棒性总失败模型本身没做鲁棒性先验训练改用IBP、SABR等可验证训练方法重训4.2 一个实战排查流程我建议拿到一个待验证模型后不要直接上形式化验证。先用随机模糊测试扫一遍快速找反例如果模糊测试已经找到反例说明模型不鲁棒没必要费劲去做完整证明。只有模糊测试找不到反例才轮到形式化验证上场去证明“确实没有反例”。如果形式化验证超时就分片验证把输入空间按关键业务区域拆分逐块做局部验证。这个流程省了我非常多时间。形式化验证虽然强大但计算代价高不能一上来就用它去做盲目搜索。4.3 我的几个独家避坑习惯每次新模型都用脚本强制打印“原始输出”和“验证邻域”两个数值防呆。验证完成后随机采样几百个邻域内的点用普通推理引擎检测是否有违反和形式化结果交叉验证。工具版本一定要锁定Z3、ERAN、α,β-CROWN这些工具更新很快不同版本行为可能不同项目文档里必须记录版本号。另外不要盲信默认参数α,β-CROWN里的松弛策略和分支倾向对结果影响很大需要按网络结构调。5. 从玩具到落地经验扩展与个人建议5.1 真实项目里形式化验证不是拦路虎而是安全兜底刚接触时我也觉得“验证整个神经网络”是个不可能完成的任务。后来真正做项目才明白实际部署并不需要一次性证明模型对所有输入都安全更常见的做法是分层验证特性级验证只针对关键敏感区域做局部鲁棒性证明比如自动驾驶里“停止”标志附近的视觉输入窗口。运行时监控在设备端部署一个轻量级验证器/监控器对即将做出的高风险决策做快速检查不通过就拒绝执行或降级到安全模式。这正好可以放到边缘计算设备上做。数据集侧校验形式化地检查数据集的覆盖度和一致性。比如可以用约束求解判断是否存在某个输入子空间在数据集中完全没有样本这类问题对“具身智能数据集质量”这类需求很有价值。5.2 后续值得投入的方向形式化方法和AI交叉的方向这几年冒出来的洼地不少大语言模型安全现在很难直接验证一个几十亿参数的Transformer但针对结构化输出、API调用协议、工具调用参数的范围做形式化约束是可能的。比如用正则、类型系统、合同式规格约束模型输出再对外部副作用做形式化检查。多智能体交互验证多机器人协同、多Agent交易协议运行在真实环境中状态空间更大但规格往往清晰适合用模型检测加AI辅助。机器人运动安全包络验证把感知到的障碍物建模为空间约束用验证器证明当前运动轨迹与所有约束无冲突。可验证训练Verifiable Training把训练和验证放到一起不再事后找补这个思路已经有不少开源实现。5.3 我自己的实操体会如果让我总结一条最朴素的经验那就是先动手跑通一个小例子比硬啃十篇论文有用得多。形式化方法这个概念听起来门槛高但实际上Z3这类求解器的API已经非常友好把一个两层神经网络编码进去半小时就能完成。等你亲手把一个“找到反例”或“证明安全”的结果跑出来再回头去读抽象解释、分支定界的论文思路会清楚得多。另外我也建议别把“完全证明”当成所有问题的唯一答案。工程系统往往是“测试监控部分验证”的混合物形式化验证负责把关键性质钉死测试负责覆盖剩余的不确定性。把工具用对地方比追求“全证明”更现实。