3. 测试环境与工具链搭建

对抗样本测试与模型安全评估,本质上是一类“可复现的受控实验”。如果直接在开发机上跑攻击脚本,很容易出现三类问题:依赖版本冲突导致攻击算法行为不一致;测试流量与生产流量混在一起,误伤线上服务;缺少资源隔离,一次大规模迭代就把 GPU 显存打满。因此本节的核心任务是搭出一套隔离、可复现、可观测的测试环境,并把模型以服务形式暴露出来,最后接入主流安全测试工具链。

一、隔离测试环境:从“能跑”到“可复现”

隔离的第一层是进程与依赖隔离。推荐用 Conda 为每个攻击框架建独立环境,因为 CleverHans、Foolbox、ART、TextAttack 对 TensorFlow / PyTorch / NumPy 的版本要求差异较大。例如 ART 对 TensorFlow 2.x 与 PyTorch 双后端支持较好,而 Foolbox 3.x 主要面向 PyTorch、TensorFlow 2 和 JAX。一个可用的环境划分如下:

# 攻击框架环境(示例,版本需按实际锁定)
conda create -n adv-attack python=3.10 -y
conda activate adv-attack
pip install torch==2.1.0 torchvision==0.16.0
pip install cleverhans==4.0.0 foolbox==3.3.4
pip install adversarial-robustness-toolbox==1.17.0
pip install textattack==0.3.10

# 被测模型服务环境(与攻击环境分离)
conda create -n model-serve python=3.10 -y
conda activate model-serve
pip install fastapi uvicorn onnxruntime-gpu==1.17.0

隔离的第二层是网络与资源隔离。测试环境应使用独立网段或 Docker 网络,模型服务只监听内网地址,例如 127.0.0.1:8000 或 172.20.0.10:8000,不直接暴露公网。GPU 资源通过 CUDA_VISIBLE_DEVICES 指定,避免攻击脚本与被测服务争抢同一张卡:

# 被测服务使用 0 号卡,攻击脚本使用 1 号卡
CUDA_VISIBLE_DEVICES=0 uvicorn serve:app --host 127.0.0.1 --port 8000
CUDA_VISIBLE_DEVICES=1 python run_attack.py

隔离的第三层是数据隔离。测试集、对抗样本、日志应放在独立目录,例如 /data/security_test/{clean,adv,logs},并记录每次实验的随机种子、框架版本、模型哈希,保证结果可追溯。

二、模型服务化部署:把模型变成可攻击的接口

安全测试工具通常需要“输入张量 → 输出 logits 或概率”的接口。最直接的方式是用 FastAPI 封装一个推理服务,暴露 /predict 接口。下面是一个最小可用示例,加载 ONNX 模型并返回 logits:

# serve.py
import numpy as np, onnxruntime as ort
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()
sess = ort.InferenceSession("model.onnx",
        providers=["CUDAExecutionProvider"])

class Req(BaseModel):
    data: list  # shape [N, C, H, W] 展平后的嵌套列表

@app.post("/predict")
def predict(req: Req):
    x = np.array(req.data, dtype=np.float32)
    logits = sess.run(None, {"input": x})[0]
    return {"logits": logits.tolist()}

启动后可用 curl 验证:

curl -X POST http://127.0.0.1:8000/predict \
  -H "Content-Type: application/json" \
  -d '{"data": [[[[0.1,0.2],[0.3,0.4]]]]}'

关键判断依据:返回的 logits 维度应与类别数一致,且对同一输入多次请求结果稳定(可关闭 dropout、固定随机种子)。若返回概率而非 logits,需在攻击脚本中做 log 变换,否则 FGSM、PGD 等基于梯度的攻击会失真。

三、常用安全测试工具的安装与配置

四类工具定位不同,配置要点也不同:

  • CleverHans:偏教学与经典攻击实现,提供 FGSM、PGD、CW 等。配置重点是 model 需为可求导的 TensorFlow/PyTorch 模型,且 clip_min/clip_max 要与输入归一化范围一致。
  • Foolbox:API 简洁,支持 LinfFastGradientAttack、L2PGD 等。配置重点是 epsilon 需按像素尺度设定,例如 [0,1] 输入下常用 8/255≈0.031。
  • ART:覆盖攻击、防御、检测、评估全流程,支持 PyTorchClassifier、TensorFlowV2Classifier 包装器。配置重点是 loss、optimizer、input_shape、nb_classes 必须与模型严格一致。
  • TextAttack:面向 NLP,通过 AttackRecipe 组合替换、插入、删除等扰动。配置重点是 model_wrapper 与 tokenizer 要匹配,且需设置 query_budget 控制查询次数。

下面给出一个 Foolbox 攻击 PyTorch 模型的配置片段,用于验证环境是否打通:

import torch, foolbox as fb
model = torch.load("model.pt").eval().cuda()
fmodel = fb.PyTorchModel(model, bounds=(0,1))
attack = fb.attacks.LinfFastGradientAttack()
eps = 8/255
adv = attack(fmodel, images, labels, epsilons=eps)
# 判断:攻击成功率应显著高于随机扰动

若攻击成功率为 0,优先检查三件事:模型是否处于 eval 模式、输入是否已归一化到 bounds 范围、标签是否为 tensor 且与输出维度对齐。

四、工具链协同关系

把上述环节串起来,就形成一条从“隔离环境”到“攻击评估”的链路。下图展示了测试环境、模型服务与四类工具之间的数据流与依赖关系:

隔离测试环境 Conda 环境隔离 Docker 网络隔离 GPU 设备隔离 数据目录隔离 /data/security_test/ clean | adv | logs 模型服务化部署 FastAPI + Uvicorn ONNX Runtime / TorchServe POST /predict 返回 logits 127.0.0.1:8000 CUDA_VISIBLE_DEVICES=0 安全测试工具链 CleverHans(经典攻击) Foolbox(简洁 API) ART(全流程评估) TextAttack(NLP 扰动) CUDA_VISIBLE_DEVICES=1 独立 conda 环境 部署 调用 攻击评估数据流 干净样本 x, y 攻击算法 FGSM / PGD / CW 对抗样本 x_adv 评估 成功率 迭代调参:epsilon / 步数 / 查询预算 环境隔离保证可复现,服务化提供统一接口,工具链负责攻击与评估

图中左侧是隔离环境,中间是模型服务,右侧是工具链;下方数据流说明一次完整评估的路径:干净样本经攻击算法生成对抗样本,再送入模型服务获取预测,最后统计攻击成功率。虚线回路表示根据成功率调整 epsilon、迭代步数或查询预算,形成迭代优化。

建议在正式测试前先跑一次“冒烟测试”:用 FGSM 在 100 张样本上攻击,若成功率在 60%~95% 之间(示例范围,随模型与 epsilon 变化),说明环境、服务、工具链已正确打通;若为 0 或 100%,优先排查归一化范围、模型 eval 模式与标签对齐。