使用IBM ART库生成交通信号牌的攻击样本
目标:生成对抗样本,扰动图像,让原本是“停”的信号牌识别为“禁止驶入”:
代码如下(注意,因为我找的cnn原始模型支持的是灰度图像,所以彩色的对抗样本还需要修改代码):
import cv2, os import numpy as np import numpy as np import tensorflow as tf from keras.models import load_model from tensorflow.keras.layers import Dense from tensorflow.keras.utils import to_categorical from art.estimators.classification import TensorFlowV2Classifier from art.attacks.evasion import FastGradientMethod import matplotlib.pyplot as plt from tensorflow.keras.models import load_model from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense from tensorflow.keras.optimizers import Adam from tensorflow.keras.utils import to_categorical from sklearn.model_selection import train_test_split def getCalssName(classNo): if classNo == 0: return 'Speed Limit 20 km/h' elif classNo == 1: return 'Speed Limit 30 km/h' elif classNo == 2: return 'Speed Limit 50 km/h' elif classNo == 3: return 'Speed Limit 60 km/h' elif classNo == 4: return 'Speed Limit 70 km/h' elif classNo == 5: return 'Speed Limit 80 km/h' elif classNo == 6: return 'End of Speed Limit 80 km/h' elif classNo == 7: return 'Speed Limit 100 km/h' elif classNo == 8: return 'Speed Limit 120 km/h' elif classNo == 9: return 'No passing' elif classNo == 10: return 'No passing for vechiles over 3.5 metric tons' elif classNo == 11: return 'Right-of-way at the next intersection' elif classNo == 12: return 'Priority road' elif classNo == 13: return 'Yield' elif classNo == 14: return 'Stop' elif classNo == 15: return 'No vechiles' elif classNo == 16: return 'Vechiles over 3.5 metric tons prohibited' elif classNo == 17: return 'No entry' elif classNo == 18: return 'General caution' elif classNo == 19: return 'Dangerous curve to the left' elif classNo == 20: return 'Dangerous curve to the right' elif classNo == 21: return 'Double curve' elif classNo == 22: return 'Bumpy road' elif classNo == 23: return 'Slippery road' elif classNo == 24: return 'Road narrows on the right' elif classNo == 25: return 'Road work' elif classNo == 26: return 'Traffic signals' elif classNo == 27: return 'Pedestrians' elif classNo == 28: return 'Children crossing' elif classNo == 29: return 'Bicycles crossing' elif classNo == 30: return 'Beware of ice/snow' elif classNo == 31: return 'Wild animals crossing' elif classNo == 32: return 'End of all speed and passing limits' elif classNo == 33: return 'Turn right ahead' elif classNo == 34: return 'Turn left ahead' elif classNo == 35: return 'Ahead only' elif classNo == 36: return 'Go straight or right' elif classNo == 37: return 'Go straight or left' elif classNo == 38: return 'Keep right' elif classNo == 39: return 'Keep left' elif classNo == 40: return 'Roundabout mandatory' elif classNo == 41: return 'End of no passing' elif classNo == 42: return 'End of no passing by vechiles over 3.5 metric tons' def grayscale(img): img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) return img def equalize(img): img =cv2.equalizeHist(img) return img def preprocessing(img): # img = grayscale(img) img = equalize(img) img = img/255 return img def read_imgs(image_dir, label=0): # 读取图片 image_files = os.listdir(image_dir) images = [] labels = [] for image_file in image_files: image_path = os.path.join(image_dir, image_file) image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) # 以灰度模式读取图片 image = cv2.resize(image, (30, 30)) img = preprocessing(image) images.append(img) labels.append(label) return images, labels def test_predict(model): # 读取图片 image_dir = 'D:/welink/STOP/before' # images = read_imgs(image_dir) image_files = os.listdir(image_dir) images = [] for image_file in image_files: image_path = os.path.join(image_dir, image_file) image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) # 以灰度模式读取图片 image = cv2.resize(image, (30, 30)) img = preprocessing(image) img = img.reshape(1, 30, 30, 1) images.append(img) # PREDICT IMAGE predictions = model.predict(img) predict_x=model.predict(img) classIndex=np.argmax(predict_x) probabilityValue =np.amax(predictions) print("img path:", image_file, " ==> ", str(classIndex)+" "+str(getCalssName(classIndex))) print(str(round(probabilityValue*100,2) )+"%") def refact_model(model): base_model = model # 移除最后的分类层 base_model = Model(inputs=base_model.input, outputs=base_model.layers[-2].output) # 添加一个新的分类层 output = Dense(2, activation='softmax', name='new_dense')(base_model.output) model = Model(inputs=base_model.input, outputs=output) # 编译模型 model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) return model def retrain_with2label(model): image_dir1 = 'D:/welink/STOP/14' image_dir2 = 'D:/welink/STOP/17' images1, labels1 = read_imgs(image_dir1, 0) images2, labels2 = read_imgs(image_dir2, 1) # 合并图片和标签 images = images1 + images2 labels = labels1 + labels2 images = np.array(images, dtype='float32') # 如果模型的输入形状是(30, 30, 1),那么我们需要增加一个维度 if model.input_shape[-1] == 1: images = np.expand_dims(images, axis=-1) labels = np.array(labels) labels = to_categorical(labels, num_classes=2) # 划分训练集和测试集 train_images, test_images, train_labels, test_labels = train_test_split(images, labels, test_size=0.2) # 训练模型 model.fit(train_images, train_labels, validation_data=(test_images, test_labels), epochs=10) def test_predict2(model): # 选择stop的图像,扰动前的 images, _ = read_imgs('D:/welink/STOP/before') if model.input_shape[-1] == 1: images = np.expand_dims(images, axis=-1) preds = model.predict(images) print('Predicted before:', preds.argmax(axis=1)) return images def run_art(images): # 创建一个目标标签(我们希望模型将0 stop识别为1 no entry) target_label = to_categorical(1, num_classes=2) target_label = np.tile(target_label, (len(images), 1)) # 创建ART分类器 classifier = TensorFlowV2Classifier( model=model, nb_classes=2, input_shape=(30, 30, 1), loss_object=tf.keras.losses.CategoricalCrossentropy(from_logits=True), clip_values=(0, 1) ) # 创建FGSM实例 attack = FastGradientMethod(estimator=classifier, targeted=True) # 初始化对抗样本为原始图像 adv_images = np.copy(images) for i in range(100): # 最多迭代100次 # 生成对抗样本的扰动 perturbations = attack.generate(x=adv_images, y=target_label) - adv_images # 计算所有样本的平均扰动 avg_perturbation = np.mean(perturbations, axis=0) # 将平均扰动添加到所有对抗样本上 adv_images += avg_perturbation # 使用模型对对抗样本进行预测 preds = model.predict(adv_images) print('Iteration:', i, 'Predicted after:', preds.argmax(axis=1)) # 如果所有的预测结果都为1,那么停止迭代 if np.all(preds.argmax(axis=1) == 1): break # 保存对抗样本 for i in range(len(adv_images)): # 将图像的数据类型转换为uint8,并且将图像的值范围调整到[0, 255] img = (adv_images[i] * 255).astype(np.uint8) # 保存图像 cv2.imwrite(f'traffic_adv_image_{i}.png', img) # 归一化平均扰动并保存为图像 avg_perturbation = (avg_perturbation - np.min(avg_perturbation)) / (np.max(avg_perturbation) - np.min(avg_perturbation)) # 将平均扰动的值范围调整到[0, 255],并转换为uint8类型 avg_perturbation = (avg_perturbation * 255).astype(np.uint8) # 将灰度图像转换为RGB图像 avg_perturbation_rgb = cv2.cvtColor(avg_perturbation, cv2.COLOR_GRAY2RGB) # 保存图像 cv2.imwrite('traffic_avg_perturbation.png', avg_perturbation_rgb) if __name__ == "__main__": # 找到一个训练好的,识别交通信号牌的模型: https://github.com/Daulettulegenov/TSR_CNN model = load_model(r'D:\source\competition\TSR_CNN-main\CNN_model_3.h5') # 预测原始的输出类型,可以看到并不能正确的分类,因为是中文字幕 停!!!!而不是 STOP test_predict(model) # 因此,需要迁移训练,让其识别中文的“停” model = refact_model(model) # 测试是否可以识别中文的停 retrain_with2label(model) # 预测新的输出类型,可以看到能正确的分类,即便是中文的停!!! images = test_predict2(model) # 生成扰动图像,让其扰动,识别为no entry,保存扰动图像 run_art(images)
效果:
Predicted before: [0 0 0 0 0 0 0 0 0 0]
output, from_logits = _get_logits(....
1/1 [==============================] - 0s 38ms/step
Iteration: 0 Predicted after: [1 1 0 1 0 0 1 0 1 0]
1/1 [==============================] - 0s 46ms/step
Iteration: 1 Predicted after: [1 1 1 1 1 1 1 1 1 1]
生成的对抗样本:
依稀可见“停”
扰动图像:
代码说明:这份代码的主要目的是使用深度学习模型来识别交通信号牌,并使用对抗性攻击来生成能够误导模型的对抗样本。
以下是代码的主要部分的解释:
1. getCalssName(classNo): 这个函数根据类别编号返回对应的交通信号牌的名称。
2. grayscale(img), equalize(img), preprocessing(img): 这些函数用于图像预处理,包括将图像转换为灰度图像,进行直方图均衡化,以及归一化。
3. read_imgs(image_dir, label=0): 这个函数读取指定目录下的所有图像,并返回图像数据和对应的标签。
4. test_predict(model): 这个函数读取指定目录下的所有图像,然后使用模型进行预测,并打印出预测结果。
5. refact_model(model): 这个函数修改模型的结构,移除最后的分类层,然后添加一个新的分类层。
6. retrain_with2label(model): 这个函数读取两个目录下的所有图像,然后使用这些图像和对应的标签来训练模型。
7. test_predict2(model): 这个函数读取指定目录下的所有图像,然后使用模型进行预测,并返回预测结果和图像数据。
8. run_art(images): 这个函数使用对抗性攻击来生成对抗样本,并保存对抗样本和平均扰动。
在if __name__ == "__main__"部分,代码首先加载一个预训练的模型,然后使用这个模型来预测原始的交通信号牌图像。然后,代码修改模型的结构,并使用新的数据来训练模型。最后,代码使用新的模型来预测原始的交通信号牌图像,并使用对抗性攻击来生成对抗样本。
1. getCalssName(classNo): 这个函数根据类别编号返回对应的交通信号牌的名称。
2. grayscale(img), equalize(img), preprocessing(img): 这些函数用于图像预处理,包括将图像转换为灰度图像,进行直方图均衡化,以及归一化。
3. read_imgs(image_dir, label=0): 这个函数读取指定目录下的所有图像,并返回图像数据和对应的标签。
4. test_predict(model): 这个函数读取指定目录下的所有图像,然后使用模型进行预测,并打印出预测结果。
5. refact_model(model): 这个函数修改模型的结构,移除最后的分类层,然后添加一个新的分类层。
6. retrain_with2label(model): 这个函数读取两个目录下的所有图像,然后使用这些图像和对应的标签来训练模型。
7. test_predict2(model): 这个函数读取指定目录下的所有图像,然后使用模型进行预测,并返回预测结果和图像数据。
8. run_art(images): 这个函数使用对抗性攻击来生成对抗样本,并保存对抗样本和平均扰动。
在if __name__ == "__main__"部分,代码首先加载一个预训练的模型,然后使用这个模型来预测原始的交通信号牌图像。然后,代码修改模型的结构,并使用新的数据来训练模型。最后,代码使用新的模型来预测原始的交通信号牌图像,并使用对抗性攻击来生成对抗样本。
样本下载:
交通信号灯 https://www.kaggle.com/datasets/meowmeowmeowmeowmeow/gtsrb-german-traffic-sign