使用Cleverhans的CarliniWagnerL2攻击TensorFlow模型时触发NotImplementedError
错误原因分析
你遇到的NotImplementedError核心问题是:CarliniWagnerL2(CW-L2)攻击依赖模型输出logits(即softmax激活层之前的原始预测得分),但你用CallableModelWrapper包装Keras模型时,指定了'probs'(也就是softmax后的概率输出),这就导致Cleverhans无法获取攻击必须的logits,从而触发错误。
解决方案步骤
要解决这个问题,我们需要调整模型结构和Cleverhans的模型包装方式,确保Cleverhans能拿到logits输出:
调整Keras模型,输出logits而非直接输出概率
CW-L2攻击不需要经过softmax的概率值,而是需要原始的logits。所以我们可以修改模型,让最后一层Dense不添加softmax激活,然后在编译模型时指定from_logits=True参数,告诉损失函数我们传入的是logits。使用Cleverhans专为Keras设计的
KerasModelWrapper
相比通用的CallableModelWrapper,KerasModelWrapper能更好地适配Keras模型,自动处理logits的获取,不需要手动实现额外方法。
修改后的完整代码
# TensorFlow and tf.keras import tensorflow as tf # Cleverhans import cleverhans as ch from cleverhans.attacks import CarliniWagnerL2 from cleverhans.model import KerasModelWrapper # Others import numpy as np # 注意:Cleverhans旧版本需要显式设置TensorFlow会话 sess = tf.Session() tf.compat.v1.keras.backend.set_session(sess) # 加载数据集 mnist = tf.keras.datasets.mnist (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images / 255.0 test_images = test_images / 255.0 # 构建模型:最后一层不使用softmax,输出logits model = tf.keras.Sequential([ tf.keras.layers.Flatten(input_shape=(28, 28)), tf.keras.layers.Dense(128, activation=tf.nn.relu), tf.keras.layers.Dense(10) # 这里去掉softmax,输出logits ]) # 编译模型时指定from_logits=True,因为我们传入的是logits model.compile(optimizer='SGD', loss='sparse_categorical_crossentropy', metrics=['accuracy'], from_logits=True) # 训练模型 model.fit(train_images, train_labels, epochs=3) # 使用KerasModelWrapper包装模型,自动适配logits输出 wrap = KerasModelWrapper(model) cw = CarliniWagnerL2(wrap, sess=sess) # 设置CW攻击参数 cw_params = { 'batch_size': 1, 'confidence': 10, 'learning_rate': 0.1, 'binary_search_steps': 5, 'max_iterations': 1000, 'abort_early': True, 'initial_const': 0.01, 'clip_min': 0, 'clip_max': 1 } # 生成对抗样本 image = np.array([test_images[0]]) adv_cw = cw.generate_np(image, **cw_params)
Cleverhans兼容的模型类型说明
Cleverhans对模型的核心要求是能够提供logits输出,针对不同框架的模型,推荐对应的包装器:
- 对于Keras模型:优先使用
KerasModelWrapper,它会自动识别模型的输出是否为logits,不需要额外配置。 - 对于原生TensorFlow模型:可以使用
Model基类自定义模型,实现fprop方法并返回包含'logits'键的字典;或者用CallableModelWrapper,但需要确保传入的callable函数返回logits,而非概率。 - 对于PyTorch模型:使用
PyTorchModelWrapper(需要对应版本的Cleverhans支持)。
如果坚持要使用输出概率的模型,你需要手动实现get_logits方法,或者在fprop中显式定义logits输出,但这种方式比较繁琐,不如直接调整模型输出logits来得高效。
内容的提问来源于stack exchange,提问作者Rolle

