最近项目中需要使用FGSM攻击方法,使用主流的foolbox和cleverhans中的攻击方法时,发现模型不匹配,所以不能继续使用,网上找到的代码也不能使用,需要自己实现一个。
根据论文 Explaining and Harnessing Adversarial Examples,FGSM的原理是损失函数对图像x求梯度,所以先求得损失函数,之后再对图像x求导,获得梯度,代码如下。
其中噪声的计算方法如下所示,ε 表示扰动的大小,L 表示损失函数。
通过查找资料和讨论,得出了如下的代码。
from keras import backend, losses
def fgsm(model, image, y_true, eps=0.1):
y_pred = model.output
# y_true: 目标真实值的张量。
# y_pred: 目标预测值的张量。
loss = losses.categorical_crossentropy(y_true, y_pred)
gradient = backend.gradients(loss, model.input)
gradient = gradient[0]
adv = image + backend.sign(gradient) * eps #fgsm算法
sess = backend.get_session()
adv = sess.run(adv, feed_dict={ model.input : image}) #注意这里传递参数的情况
adv = np.clip(adv, 0, 255) #有的像素点会超过255,需要处理
return adv
# fgsm攻击 函数调用
# 下面部分代码(图像处理)需要根据自己的攻击图像的实际情况进行修改
def fgsm_attack(img, epsilons = 1000):
image = cv2.imread(img)
if image is None:
print(img, end=' ')
print("图像读取失败")
return False
# 加载准备攻击的模型,对要攻击的图形进行转换
lpr_model = xxx
img_convert = cv2.resize(image, (x,y)) # 这里的x/y根据要求进行修改
ret_predict = lpr_model.predict(np.array([img_convert])) #进行预测
# 获取预测结果的one-hot编码,在攻击时需要用到
label = np.zeros([1, 10, 1, 10]) #根据自己的情况进行修改
for i in range(ret_predict.shape[0]):
pos = res[i]
label[0][i][0][pos] = 1
# 计算eps的值
epsilons = np.linspace(0,1,num=epsilons+1)[1:]
print("开始使用fgsm进行攻击")
for eps in epsilons:
img_attack = fgsm(lpr_model, img_convert, label, eps=eps)
attack = lpr_model.predict(img_attack)
# 当识别的结果不等时,表示攻击成功
if attack != ret_predict:
print('攻击成功,攻击后的结果为:', attack)
cv2.imwrite("attack_result.jpg", img_ret)
return True
return False
if __name__ == "__main__":
fgsm_attack("./images/1.jpg")
参考:
- stackoverflow问答 https://stackoverflow.com/questions/39561560/getting-gradient-of-model-output-w-r-t-weights-using-keras
- keras的官方issue https://github.com/keras-team/keras/issues/2226
- stackoverflow问答 https://stackoverflow.com/questions/54480208/get-gradient-values-in-a-cnn-with-keras-tf