感慨:初学mxnet 和 python ,写出来一个FGSM真的坑太多了。
FGSM的原理很简单,利用输入层的梯度作为扰动,改变输入图像,从而达到肉眼看不到区别的前提下能够使得神经网络错分。
原理博客:
https://blog.csdn.net/u014038273/article/details/78773515
mxnet example版本,不定向生成。(ps:这个我觉得是有点问题的。就产生一次扰动就能确保错分了???)
我自己的未成熟的版本:(还有一些问题待解决,现放出来看看有么有大神指点)
if __name__ == '__main__':
# load an image
batchSize = 1
datashape = (1, 144, 128)
ctx = mx.gpu(device_id=2)
data_iter = mx.io.ImageRecordIter(path_imgrec='./webface/train_webface_train.rec',data_shape=datashape, batch_size=batchSize)
data_iter.reset()
batch = data_iter.next()
# load model
modelPrefix = '/netname' # model name
modelEpoch = 20
# net = import_module('symbols.' + model)
sym1, arg_params, aux_params = mx.model.load_checkpoint(modelPrefix, modelEpoch)
mod = mx.mod.Module(sym1,context=ctx)
mod.bind(data_shapes=[('data', (1, 1, 144, 128))], for_training=True, inputs_need_grad=True)
mod.set_params(arg_params=arg_params, aux_params=aux_params)
# 备份
mod_copy = mx.mod.Module(sym1,context=ctx)
mod_copy.bind(data_shapes=[('data', (1, 1, 144, 128))], for_training=True, inputs_need_grad=True)
mod_copy.set_params(arg_params=arg_params, aux_params=aux_params)
# 开始利用FGSM生成样本
examples=210000 #生成样本数
for index in range(examples):
print("the %d pic \n"%(index))
# first bp 利用当前网络做一次预测,做为比照
mod.forward(batch, is_train=True)
out = mod.get_outputs()[0].asnumpy()
# print("ouput: ", mod.get_outputs()[0].asnumpy().shape)
maxValue = 0
maxIndex = 0
for i in range(len(out[0])):
# print(out[0][i])
if out[0][i] > maxValue:
maxValue = out[0][i]
maxIndex = i
trueLabel = maxIndex
print("the true label is %s"%(trueLabel))
## --------------------FGSM-------
ratio = 0.75
iterNum=200
originalData = copy.deepcopy(batch.data) #必须用深复制,list的浅复制是引用
image = batch.data[0]
originalBatch = mx.io.DataBatch(data=originalData, label=batch.label)
image = image[0].asnumpy()
plt.subplot(1, 3, 1)
plt.imshow(image[0].astype(np.uint8),cmap='gray')
# 定向的生成 选择一个错误的label
batch.label[0][0] = 10575 - batch.label[0][0]
for iter in range(iterNum):
# get the grad for input layer
mod.backward()
grad = mod.get_input_grads()[0].asnumpy()
# add the noise to raw pic
noise = np.sign(grad)
raw_batch = mx.io.DataBatch(data=copy.deepcopy(batch.data), label=batch.label)
raw_output = raw_batch.data[0].asnumpy() + ratio * noise
raw_batch.data[0][:] = raw_output
# predict the new pic deepcopy
mod_copy.forward(raw_batch, is_train=False)
out = mod_copy.get_outputs()[0].asnumpy()
maxValue = 0
maxIndex = 0
for i in range(len(out[0])):
# print(out[0][i])
if out[0][i] > maxValue:
maxValue = out[0][i]
maxIndex = i
batch = raw_batch
mod.forward(batch, is_train=True)
if iter == 10 & trueLabel == maxIndex:
ratio=1.5
if iter == 20 & trueLabel == maxIndex:
ratio = 2
if trueLabel != maxIndex:
print("the %d iteration is %f \n" % (iter, maxValue))
print("the true label is \t", trueLabel)
print("the predict label is ", maxIndex)
print("\n")
image2 = raw_batch.data[0]
image2 = image2[0].asnumpy()
image_deta = image - image2
plt.subplot(1, 3, 2)
plt.imshow(image2[0].astype(np.uint8),cmap='gray')
plt.subplot(1, 3, 3)
plt.imshow(image_deta[0].astype(np.uint8),cmap='gray')
cv2.imwrite("./fgsm/original_"+str(trueLabel)+".jpg",image[0])
cv2.imwrite("./fgsm/"+str(trueLabel)+"_"+str(maxIndex)+".jpg",image2[0])
# plt.show()
break
data_iter.iter_next()
batch = data_iter.next()
这里面的几个问题需要记录一下:
1.生成步骤
step 1:加载网络,加载原始样本
step 2 :对样本做前向预测,作为baseline用。
step 3 :得到输入的梯度,加上sign函数最大化扰动,产生噪声。
step 4 :在原始图像上加上噪声。
step 5 :对新图像在原始网络中,前向运算预测类别。
step 6 :错分则保留,否则继续迭代,直到达到迭代上限。
2.几个问题
第一个,生成样本速度。
在实现的过程中,我发现有的图片通过一次迭代就能产生对抗样本。而有的图片通过无数次迭代都是无法产生的。
这个问题我觉跟样本本身有关,本来就易错分的样本容易受干扰影响。
第二个,生成样本质量。
在图片上,有的对抗样本确实和原始样本差异较小,肉眼无法区分。而有的却比较大,感觉到了图片发生了改变。
第三个,对抗样本的意义。
一般为了增强某个网络的鲁棒性,生成对抗样本加入到原始训练集中一起训练,可以增强对某些扰动的鲁棒性。但是,我觉得这种效果是微小的乃至无效的。一来是对抗样本的质量无法控制,二来是对抗样本的生成依旧是训练集的一种扩充,提升的效果可以看为数据集扩充带来的效果。
第四个,再FGSM的过程中,mxnet的backward函数需要先进行一次forward,那么此时的网络是否更新了。
第五个,如何量化这个生成质量,如何提高速度,控制迭代,如何选择扰动系数。
————————————————————————————————————update-------------------------------
几个小体会。
1、mxnet api:
forward 设置 is_train 为true和false 的时候,计算结果不一样。
forward 将is_train 设置为false 时的结果和用predict的计算结果是一致的。
这个看不到源码,也不知道为什么,可能需要找找。
2、深浅复制
list的深复制要用,copy.deepcopy()
ndarray的深复制,要用.copy()
----------------------------------------------------------------------update---------------------------------------------------------------------
感谢lq大神,嗯终于找到了问题。
问题复述:
之前不论是定向生成还是不定向生成,都存在一个问题。无法收敛,难以生成对抗样本。
解决:
加入噪声的过程该是减去的过程,而不是加的过程。这个原理如果利用方向传播,减小loss,就是减去梯度来想其实是类似的。但是在论文中是+,包括部分复现的代码都是+。我觉得也是有道理的,假如从不定向的角度来想,我们需要最大化这个正确分类的loss,所以是加上扰动。从定向的角度来想,应该是最小化错误分类的loss,所以是减去扰动。
但是,在代码复现的过程中,发现用减去是无法收敛的。所以还需要多想想为什么。
更新代码:
if __name__ == '__main__':
# load an image
batchSize = 1
datashape = (1, 144, 128)
ctx = mx.gpu(device_id=2)
data_iter = mx.io.ImageRecordIter(path_imgrec='./webface/train_webface_train.rec',data_shape=datashape, batch_size=batchSize)
data_iter.reset()
batch = data_iter.next()
# load model
modelPrefix = '/netname' # model name
modelEpoch = 20
# net = import_module('symbols.' + model)
sym1, arg_params, aux_params = mx.model.load_checkpoint(modelPrefix, modelEpoch)
mod = mx.mod.Module(sym1,context=ctx)
mod.bind(data_shapes=[('data', (1, 1, 144, 128))], for_training=True, inputs_need_grad=True)
mod.set_params(arg_params=arg_params, aux_params=aux_params)
# 备份
mod_copy = mx.mod.Module(sym1,context=ctx)
mod_copy.bind(data_shapes=[('data', (1, 1, 144, 128))], for_training=True, inputs_need_grad=True)
mod_copy.set_params(arg_params=arg_params, aux_params=aux_params)
# 开始利用FGSM生成样本
examples=210000 #生成样本数
for index in range(examples):
print("the %d pic \n"%(index))
# first bp 利用当前网络做一次预测,做为比照
mod.forward(batch, is_train=True)
out = mod.get_outputs()[0].asnumpy()
# print("ouput: ", mod.get_outputs()[0].asnumpy().shape)
maxValue = 0
maxIndex = 0
for i in range(len(out[0])):
# print(out[0][i])
if out[0][i] > maxValue:
maxValue = out[0][i]
maxIndex = i
trueLabel = maxIndex
print("the true label is %s"%(trueLabel))
## --------------------FGSM-------
ratio = 0.75
iterNum=30
originalData = copy.deepcopy(batch.data) #必须用深复制,list的浅复制是引用
image = batch.data[0]
originalBatch = mx.io.DataBatch(data=originalData, label=batch.label)
image = image[0].asnumpy()
plt.subplot(1, 3, 1)
plt.imshow(image[0].astype(np.uint8),cmap='gray')
# 定向的生成 选择一个错误的label
batch.label[0][0] = 10575 - batch.label[0][0]
for iter in range(iterNum):
# get the grad for input layer
mod.backward()
grad = mod.get_input_grads()[0].asnumpy()
# add the noise to raw pic
noise = np.sign(grad)
raw_batch = mx.io.DataBatch(data=copy.deepcopy(batch.data), label=batch.label)
raw_output = raw_batch.data[0].asnumpy() -ratio * noise
raw_batch.data[0][:] = raw_output
# predict the new pic deepcopy
mod_copy.forward(raw_batch, is_train=False)
out = mod_copy.get_outputs()[0].asnumpy()
maxValue = 0
maxIndex = 0
for i in range(len(out[0])):
# print(out[0][i])
if out[0][i] > maxValue:
maxValue = out[0][i]
maxIndex = i
batch = raw_batch
mod.forward(batch, is_train=True)
if iter == 10 & trueLabel == maxIndex:
ratio=1.5
if iter == 20 & trueLabel == maxIndex:
ratio = 2
if trueLabel != maxIndex:
print("the %d iteration is %f \n" % (iter, maxValue))
print("the true label is \t", trueLabel)
print("the predict label is ", maxIndex)
print("\n")
image2 = raw_batch.data[0]
image2 = image2[0].asnumpy()
image_deta = image - image2
plt.subplot(1, 3, 2)
plt.imshow(image2[0].astype(np.uint8),cmap='gray')
plt.subplot(1, 3, 3)
plt.imshow(image_deta[0].astype(np.uint8),cmap='gray')
cv2.imwrite("./fgsm/original_"+str(trueLabel)+".jpg",image[0])
cv2.imwrite("./fgsm/"+str(trueLabel)+"_"+str(maxIndex)+".jpg",image2[0])
# plt.show()
break
data_iter.iter_next()
batch = data_iter.next()
主要的更新:
raw_output = raw_batch.data[0].asnumpy() -ratio * noise
还有些小的代码优化,就不需要放出了。