TensorFlow PGD攻击报错:标签与输入张量形状不兼容求助
问题根源与修复方案
错误核心原因
- PGD函数逻辑错误:当前代码直接用输入图像
adv_image和标签计算交叉熵,但adv_image是(1,32,32,3)的图像张量,不是模型的输出结果(模型输出应对应8类的概率/ logits,形状为(1,8)),这才是形状不匹配的根本原因,不需要把标签转成图像形状。 - 标签格式不匹配:使用
categorical_crossentropy需要标签是one-hot编码格式,但你的标签是单个整数,必须先做格式转换。
具体修复步骤
步骤1:修正PGD攻击函数的loss计算逻辑
计算loss前,必须先让模型对adv_image做预测,用模型的输出结果和标签计算损失,而非直接用图像张量。
步骤2:将整数标签转换为one-hot编码
标签取值0-7(共8类),需把单个整数转成形状为(1,8)的one-hot向量。
修复后的完整代码
修复后的PGD攻击函数
def pgd_attack(model, input_image, input_label= None, epsilon=0.0313, num_steps=20, step_size=0.002, clip_value_min=0., clip_value_max=1.0, soft_label=False, from_logits= False): loss_fn = tf.keras.losses.categorical_crossentropy if type(input_image) is np.ndarray: input_image = tf.convert_to_tensor(input_image) # 处理标签:整数转one-hot编码 if input_label is not None: if type(input_label) is np.ndarray: input_label = tf.convert_to_tensor(input_label) # 调整形状后转one-hot,类别数固定为8(0-7) input_label = tf.reshape(input_label, (1,)) input_label = tf.one_hot(input_label, depth=8) # 随机初始化对抗样本 random_noise = tf.random.uniform(shape=input_image.shape, minval=-epsilon, maxval=epsilon) adv_image = input_image + random_noise for _ in range(num_steps): with tf.GradientTape(watch_accessed_variables=False) as tape: tape.watch(adv_image) # 关键修复:先获取模型对对抗样本的输出 model_output = model(adv_image, training=False) if not soft_label: loss = loss_fn(input_label, model_output, from_logits=from_logits) else: # 软标签模式下,用模型预测的类别作为攻击目标 pred_label = tf.math.argmax(model_output, axis=1) pred_label = tf.one_hot(pred_label, depth=8) loss = loss_fn(pred_label, model_output, from_logits=from_logits) gradient = tape.gradient(loss, adv_image) adv_image = adv_image + step_size * tf.sign(gradient) adv_image = tf.clip_by_value(adv_image, input_image-epsilon, input_image+epsilon) adv_image = tf.clip_by_value(adv_image, clip_value_min, clip_value_max) adv_image = tf.stop_gradient(adv_image) return adv_image
调用代码优化(避免索引越界)
for i in range(20): # 修正randint范围,避免索引越界 n = random.randint(0, len(data_manager.X_test)-1) label = data_manager.y_test[n] img = data_manager.X_test[n] img = tf.expand_dims(img, axis=0) img = img.astype('float32') true_pred = our_network_skip.predict(img) # 直接传入整数标签即可 pa = pgd_attack(our_network_skip, img, label, epsilon=0.0313, num_steps=20, step_size=0.002, clip_value_min=0., clip_value_max=1.0, soft_label=False, from_logits= False) pgd_pred = our_network_skip.predict(pa) # 优化打印格式,显示真实类别和对抗样本预测类别 print("True label: {}, adversarial label: {}".format( tf.argmax(true_pred, axis=1).numpy()[0], tf.argmax(pgd_pred, axis=1).numpy()[0] ))
额外说明
- 如果模型最后一层是无激活的Dense层(输出logits),需将
from_logits=True传入PGD函数,损失函数会自动适配logits计算。 - 原代码中
random.randint(0, len(data_manager.X_test))会导致索引越界,因为randint是闭区间,所以调整为len(data_manager.X_test)-1。
内容的提问来源于stack exchange,提问作者quickhelp
相关产品推荐
相关产品推荐

