利用python实现深度学习生成对抗样本,为任一图片加扰动并恢复原像素

  • 一、前言
    • (一)什么是深度学习
    • (二)什么是样本模型
    • (三)什么是对抗样本
      • 1、对抗的目的
      • 2、谁来对抗?
      • 3、对抗的敌人是谁?
      • 4、怎么对抗?
        • (1)这里有两个样本模型
        • (2)用来训练图片的样本模型和加扰动的关系
        • (3)在这个过程中,我们要做的是什么?
  • 二、实现代码
    • (一)获取样本模型
      • 1、选取样本
      • 2、训练模型
    • (二)处理图片(即生成对抗图片)
    • (三)还图片至原像素大小
    • (四)进一步恢复目视效果(可能会适得其反)
  • 三、总结
  • 四、附件下载

一、前言

每个语言都有自己的独到之处,C适合嵌入式开发,C++适合桌面端软件,java适合移动端应用,js适合网页,而python,则是为了机器学习,人工只能,应运而生似得。
这次尝试通过python,实现对抗样本模型的生成,并依据此样本,处理任一图片,并实现图片的尺寸还原,做一下记录。
首先,对一些基本概念解释一下,我只通过我的理解记录,可能不够准确,专业解释请自行百度。

(一)什么是深度学习

机器学习,举例来说,比如为了让计算机能识别出一张图片中有一只鸟,他需要进行学习,学习的过程不同于人类的思维,他是通过大量的整理好的鸟的图片,通过类似数字图像处理的方法,将每张图片分解成一个个特征,他通过记忆这些特征,并且分析出某些特征,如果在大量的鸟的图片中,都曾出现过的,计算机便认为这些特征,便是识别图片中有一只鸟的重要特征,这时候丢给计算机一张图片,如果此时图片上也能搜寻到类似的特征,计算机便把这张图片识别成鸟的图片。

(二)什么是样本模型

样本模型,则是刚刚提到的,通过大量图片,识别出来的,某一物品的重要特征的所组成的库。当然,这个库里可以包含更多物品的重要特征,不见得只有鸟或者狒狒。

(三)什么是对抗样本

对抗样本就是基于样本模型,即这个特征库已经可以较高概率的通过其所记录的特征,来较为准确的描述某一物品的前提下,进行的操作。
说到对抗样本,要搞清楚几个问题,非常重要,不然很难理解,网络上掉书袋的解释非常多,不好懂,我尝试口水解释,当然可能会有偏差,专业解释还请自行百度。

1、对抗的目的

就是给一个训练好的样本模型,任意一张包含在此模型的物品类型的图片,比如一只小鸟的图片,他能准确识别出这是一只鸟,但是经过对这张图片的一些处理后,这个样本模型或者别的包含此类物品的样本模型,竟然都把他识别成了大象,到这就结束了?不,重头戏在后面,这张图片,此时要不影响人眼的目视效果,即人眼看着没什么变化,而计算机却已经认不出这张图片来,这样才算是对抗成功了。

2、谁来对抗?

谁来对抗,当然是你选中的那张,要通过样本模型来识别的图片。

3、对抗的敌人是谁?

划重点,所指的对抗,到底对抗的是什么?对抗的其实是试图来识别这张图片上面的物品类型的那个样本模型,那么就说得清楚了,网上那些牛逼哄哄的名词,什么白盒攻击,黑盒攻击,到底是什么意思了。
白盒攻击,解释起来就是以己之矛,攻己之盾,没错,就是拿自己的模型,来识别这张图片,正常情况下,当然是这个意思就是,测试一下对抗样本的加扰动的成功与否。
黑盒攻击,就是以彼之矛,攻己之盾,就是用别人家训练的样本模型,即通过不同的样本,训练出来的模型,比如这个模型里也有鸟这个物品,用他来尝试识别这张图片,是否能识别出图片中那是一只可爱的小鸟。

4、怎么对抗?

就是刚刚说到的,怎么处理这个图片,即给图片如何加上扰动。这个就是诸位大神们,研究的重点了。方法不少,有各种算法,有名的比如FGSM、IGSM、DeepFool、JSMA等等,实现这些算法的方法也简单,都不用自己写了,直接调用就能实现,直接调用art包,这个art包,是IBM公司开发的AE工具箱(Adversarial Robustness Toolbox),python下可以直接通过pip3工具安装。
以下是重点!!!
但是我要说的不仅如此,而是我刚一接触深度学习时,困扰了我很久的一个问题,就是如果只是给图片加上扰动,跟样本模型有啥关系?我是不是用不着样本模型,直接加扰动,跳过这一步也行?因为网上能够搜到的恶意样本的相关解释也很单一,都没强调这方面的解释,也无怪乎我这样的自学者,有此困扰了。有几个问题,需要特意解释一下。

(1)这里有两个样本模型

一个样本模型是用来训练图片的样本模型,即用来处理图片的样本模型,另一个是被攻击的样本模型,即用来测试的样本模型,当然,这两个可以是一个,就是白盒和黑盒攻击的区别。

(2)用来训练图片的样本模型和加扰动的关系

给图片加扰动,到底需不需要样本模型?或者说样本模型在这个过程中,起了一个什么作用?答案当然是肯定的,需要,而且是决定性的作用。我大致可以这么理解,给图片加扰动,实质上就是在给图片,与别的物品的特征进行拟合,甚至多次拟合,什么是拟合?简单的说,就是把图片上的某些鸟的特征,给杂糅进大猩猩的特征,这里本来会被识别成鸟翅膀的,因为拟合了大猩猩的特征,变成了大猩猩的手臂,从而这张图片就被识别成了大猩猩。就是说,样本模型在其中起到的作用,就是选定一个别的物品的特征,将其记录的特征,拟合进这张图片中去,以至于这张图片会被识别成别的物品。

(3)在这个过程中,我们要做的是什么?

在刚刚解释的拟合概念中,就会出现一个拟合度的问题,即大猩猩的特征这么多,加多少到这张图片中去,这是一个很重要的问题,加多了,影响了人的目视效果,不影响目视效果,是一切的前提,加少了,就会提高了被对抗的样本模型的识别正确的概率。因此,就需要在人眼的识别度和计算机的识别度之间取一个平衡,这个度,就是拟合度,就是我们需要不断调整的一个值了。

二、实现代码

刚刚花了大篇幅来解释清楚了几个概念,我以为非常重要,所以不厌其烦。
下面,我将要带着大家来尝试,实现整个过程。另,文末附上全文代码,包含两个训练好的模型。

(一)获取样本模型

1、选取样本

样本的选择余地挺大的,我看中了两种样本,一个是cifar10,另一个是inception_v3,选前者是因为我想尝试自己生成训练模型,选择后者是因为这是谷歌的现成的训练模型,成熟,并且训练的图片的像素高,在299299,而前者的模型的图片像素是3232,实际使用中,我选后者。

2、训练模型

训练模型,我就尝试用cifar10训练一个自己的模型了,关于这个模型,特别解释一下,如果直接用keras进行训练,如果本地没有这个样本库,就会自动下载这个样本库,然而这个过程非常的漫长且容易失败,因此,推荐自行下载这个训练样本库,下载文件cifar-10-batches-py.tar.gz(文末附上下载链接),注意,划重点,你所下载的此样本库的名称可以是别的名字,但是下载以后,务必改成这个名字,而且必须放在指定目录下,在ubuntu下的目录为/home/XXX/.keras/datasets/cifar-10-batches-py.tar.gz,在windows下记不清了,反正大概是用户文件夹下的,/.keras/datasets/cifar-10-batches-py.tar.gz,这个目录,没错,windows也是用.gz结尾的压缩包。只有在这个位置,才能被keras默认识别到。
好了,直接上训练的代码。

"""
#Trains a ResNet on the CIFAR10 dataset.
ResNet v1:
[Deep Residual Learning for Image Recognition
](https://arxiv.org/pdf/1512.03385.pdf)
ResNet v2:
[Identity Mappings in Deep Residual Networks
](https://arxiv.org/pdf/1603.05027.pdf)
Model|n|200-epoch accuracy|Original paper accuracy |sec/epoch GTX1080Ti
:------------|--:|-------:|-----------------------:|---:
ResNet20   v1|  3| 92.16 %|                 91.25 %|35
ResNet32   v1|  5| 92.46 %|                 92.49 %|50
ResNet44   v1|  7| 92.50 %|                 92.83 %|70
ResNet56   v1|  9| 92.71 %|                 93.03 %|90
ResNet110  v1| 18| 92.65 %|            93.39+-.16 %|165
ResNet164  v1| 27|     - %|                 94.07 %|  -
ResNet1001 v1|N/A|     - %|                 92.39 %|  -
 
Model|n|200-epoch accuracy|Original paper accuracy |sec/epoch GTX1080Ti
:------------|--:|-------:|-----------------------:|---:
ResNet20   v2|  2|     - %|                     - %|---
ResNet32   v2|N/A| NA    %|            NA         %| NA
ResNet44   v2|N/A| NA    %|            NA         %| NA
ResNet56   v2|  6| 93.01 %|            NA         %|100
ResNet110  v2| 12| 93.15 %|            93.63      %|180
ResNet164  v2| 18|     - %|            94.54      %|  -
ResNet1001 v2|111|     - %|            95.08+-.14 %|  -
"""from __future__ import print_function
import keras
from keras.layers import Dense, Conv2D, BatchNormalization, Activation
from keras.layers import AveragePooling2D, Input, Flatten
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint, LearningRateScheduler
from keras.callbacks import ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator
from keras.regularizers import l2
from keras import backend as K
from keras.models import Model
from keras.datasets import cifar10
import numpy as np
import os
import tensorflow as tf#GPU内存不足,对GPU进行按需分配
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
keras.backend.tensorflow_backend.set_session(tf.Session(config=config))# Training parameters
batch_size = 32  # orig paper trained all networks with batch_size=128
epochs = 200
data_augmentation = True
num_classes = 10# Subtracting pixel mean improves accuracy
subtract_pixel_mean = True# Model parameter
# ----------------------------------------------------------------------------
#           |      | 200-epoch | Orig Paper| 200-epoch | Orig Paper| sec/epoch
# Model     |  n   | ResNet v1 | ResNet v1 | ResNet v2 | ResNet v2 | GTX1080Ti
#           |v1(v2)| %Accuracy | %Accuracy | %Accuracy | %Accuracy | v1 (v2)
# ----------------------------------------------------------------------------
# ResNet20  | 3 (2)| 92.16     | 91.25     | -----     | -----     | 35 (---)
# ResNet32  | 5(NA)| 92.46     | 92.49     | NA        | NA        | 50 ( NA)
# ResNet44  | 7(NA)| 92.50     | 92.83     | NA        | NA        | 70 ( NA)
# ResNet56  | 9 (6)| 92.71     | 93.03     | 93.01     | NA        | 90 (100)
# ResNet110 |18(12)| 92.65     | 93.39+-.16| 93.15     | 93.63     | 165(180)
# ResNet164 |27(18)| -----     | 94.07     | -----     | 94.54     | ---(---)
# ResNet1001| (111)| -----     | 92.39     | -----     | 95.08+-.14| ---(---)
# ---------------------------------------------------------------------------
n = 3# Model version
# Orig paper: version = 1 (ResNet v1), Improved ResNet: version = 2 (ResNet v2)
version = 1# Computed depth from supplied model parameter n
if version == 1:depth = n * 6 + 2
elif version == 2:depth = n * 9 + 2# Model name, depth and version
model_type = 'ResNet%dv%d' % (depth, version)# Load the CIFAR10 data.
(x_train, y_train), (x_test, y_test) = cifar10.load_data()# Input image dimensions.
input_shape = x_train.shape[1:]# Normalize data.
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255# If subtract pixel mean is enabled
if subtract_pixel_mean:x_train_mean = np.mean(x_train, axis=0)x_train -= x_train_meanx_test -= x_train_meanprint('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
print('y_train shape:', y_train.shape)# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)def lr_schedule(epoch):"""Learning Rate ScheduleLearning rate is scheduled to be reduced after 80, 120, 160, 180 epochs.Called automatically every epoch as part of callbacks during training.# Argumentsepoch (int): The number of epochs# Returnslr (float32): learning rate"""lr = 1e-3if epoch > 180:lr *= 0.5e-3elif epoch > 160:lr *= 1e-3elif epoch > 120:lr *= 1e-2elif epoch > 80:lr *= 1e-1print('Learning rate: ', lr)return lrdef resnet_layer(inputs,num_filters=16,kernel_size=3,strides=1,activation='relu',batch_normalization=True,conv_first=True):"""2D Convolution-Batch Normalization-Activation stack builder# Argumentsinputs (tensor): input tensor from input image or previous layernum_filters (int): Conv2D number of filterskernel_size (int): Conv2D square kernel dimensionsstrides (int): Conv2D square stride dimensionsactivation (string): activation namebatch_normalization (bool): whether to include batch normalizationconv_first (bool): conv-bn-activation (True) orbn-activation-conv (False)# Returnsx (tensor): tensor as input to the next layer"""conv = Conv2D(num_filters,kernel_size=kernel_size,strides=strides,padding='same',kernel_initializer='he_normal',kernel_regularizer=l2(1e-4))x = inputsif conv_first:x = conv(x)if batch_normalization:x = BatchNormalization()(x)if activation is not None:x = Activation(activation)(x)else:if batch_normalization:x = BatchNormalization()(x)if activation is not None:x = Activation(activation)(x)x = conv(x)return xdef resnet_v1(input_shape, depth, num_classes=10):"""ResNet Version 1 Model builder [a]Stacks of 2 x (3 x 3) Conv2D-BN-ReLULast ReLU is after the shortcut connection.At the beginning of each stage, the feature map size is halved (downsampled)by a convolutional layer with strides=2, while the number of filters isdoubled. Within each stage, the layers have the same number filters and thesame number of filters.Features maps sizes:stage 0: 32x32, 16stage 1: 16x16, 32stage 2:  8x8,  64The Number of parameters is approx the same as Table 6 of [a]:ResNet20 0.27MResNet32 0.46MResNet44 0.66MResNet56 0.85MResNet110 1.7M# Argumentsinput_shape (tensor): shape of input image tensordepth (int): number of core convolutional layersnum_classes (int): number of classes (CIFAR10 has 10)# Returnsmodel (Model): Keras model instance"""if (depth - 2) % 6 != 0:raise ValueError('depth should be 6n+2 (eg 20, 32, 44 in [a])')# Start model definition.num_filters = 16num_res_blocks = int((depth - 2) / 6)inputs = Input(shape=input_shape)x = resnet_layer(inputs=inputs)# Instantiate the stack of residual unitsfor stack in range(3):for res_block in range(num_res_blocks):strides = 1if stack > 0 and res_block == 0:  # first layer but not first stackstrides = 2  # downsampley = resnet_layer(inputs=x,num_filters=num_filters,strides=strides)y = resnet_layer(inputs=y,num_filters=num_filters,activation=None)if stack > 0 and res_block == 0:  # first layer but not first stack# linear projection residual shortcut connection to match# changed dimsx = resnet_layer(inputs=x,num_filters=num_filters,kernel_size=1,strides=strides,activation=None,batch_normalization=False)x = keras.layers.add([x, y])x = Activation('relu')(x)num_filters *= 2# Add classifier on top.# v1 does not use BN after last shortcut connection-ReLUx = AveragePooling2D(pool_size=8)(x)y = Flatten()(x)outputs = Dense(num_classes,activation='softmax',kernel_initializer='he_normal')(y)# Instantiate model.model = Model(inputs=inputs, outputs=outputs)return modeldef resnet_v2(input_shape, depth, num_classes=10):"""ResNet Version 2 Model builder [b]Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known asbottleneck layerFirst shortcut connection per layer is 1 x 1 Conv2D.Second and onwards shortcut connection is identity.At the beginning of each stage, the feature map size is halved (downsampled)by a convolutional layer with strides=2, while the number of filter maps isdoubled. Within each stage, the layers have the same number filters and thesame filter map sizes.Features maps sizes:conv1  : 32x32,  16stage 0: 32x32,  64stage 1: 16x16, 128stage 2:  8x8,  256# Argumentsinput_shape (tensor): shape of input image tensordepth (int): number of core convolutional layersnum_classes (int): number of classes (CIFAR10 has 10)# Returnsmodel (Model): Keras model instance"""if (depth - 2) % 9 != 0:raise ValueError('depth should be 9n+2 (eg 56 or 110 in [b])')# Start model definition.num_filters_in = 16num_res_blocks = int((depth - 2) / 9)inputs = Input(shape=input_shape)# v2 performs Conv2D with BN-ReLU on input before splitting into 2 pathsx = resnet_layer(inputs=inputs,num_filters=num_filters_in,conv_first=True)# Instantiate the stack of residual unitsfor stage in range(3):for res_block in range(num_res_blocks):activation = 'relu'batch_normalization = Truestrides = 1if stage == 0:num_filters_out = num_filters_in * 4if res_block == 0:  # first layer and first stageactivation = Nonebatch_normalization = Falseelse:num_filters_out = num_filters_in * 2if res_block == 0:  # first layer but not first stagestrides = 2    # downsample# bottleneck residual unity = resnet_layer(inputs=x,num_filters=num_filters_in,kernel_size=1,strides=strides,activation=activation,batch_normalization=batch_normalization,conv_first=False)y = resnet_layer(inputs=y,num_filters=num_filters_in,conv_first=False)y = resnet_layer(inputs=y,num_filters=num_filters_out,kernel_size=1,conv_first=False)if res_block == 0:# linear projection residual shortcut connection to match# changed dimsx = resnet_layer(inputs=x,num_filters=num_filters_out,kernel_size=1,strides=strides,activation=None,batch_normalization=False)x = keras.layers.add([x, y])num_filters_in = num_filters_out# Add classifier on top.# v2 has BN-ReLU before Poolingx = BatchNormalization()(x)x = Activation('relu')(x)x = AveragePooling2D(pool_size=8)(x)y = Flatten()(x)outputs = Dense(num_classes,activation='softmax',kernel_initializer='he_normal')(y)# Instantiate model.model = Model(inputs=inputs, outputs=outputs)return modelif version == 2:model = resnet_v2(input_shape=input_shape, depth=depth)
else:model = resnet_v1(input_shape=input_shape, depth=depth)model.compile(loss='categorical_crossentropy',optimizer=Adam(learning_rate=lr_schedule(0)),metrics=['accuracy'])
model.summary()
print(model_type)# Prepare model model saving directory.
save_dir = os.path.join(os.getcwd(), 'saved_models')
model_name = 'cifar10_%s_model.h5' % model_type
if not os.path.isdir(save_dir):os.makedirs(save_dir)
filepath = os.path.join(save_dir, model_name)# Prepare callbacks for model saving and for learning rate adjustment.
checkpoint = ModelCheckpoint(filepath=filepath,monitor='val_acc',verbose=1,save_best_only=True)lr_scheduler = LearningRateScheduler(lr_schedule)lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),cooldown=0,patience=5,min_lr=0.5e-6)callbacks = [checkpoint, lr_reducer, lr_scheduler]# Run training, with or without data augmentation.
if not data_augmentation:print('Not using data augmentation.')model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs,validation_data=(x_test, y_test),shuffle=True,callbacks=callbacks)
else:print('Using real-time data augmentation.')# This will do preprocessing and realtime data augmentation:datagen = ImageDataGenerator(# set input mean to 0 over the datasetfeaturewise_center=False,# set each sample mean to 0samplewise_center=False,# divide inputs by std of datasetfeaturewise_std_normalization=False,# divide each input by its stdsamplewise_std_normalization=False,# apply ZCA whiteningzca_whitening=False,# epsilon for ZCA whiteningzca_epsilon=1e-06,# randomly rotate images in the range (deg 0 to 180)rotation_range=0,# randomly shift images horizontallywidth_shift_range=0.1,# randomly shift images verticallyheight_shift_range=0.1,# set range for random shearshear_range=0.,# set range for random zoomzoom_range=0.,# set range for random channel shiftschannel_shift_range=0.,# set mode for filling points outside the input boundariesfill_mode='nearest',# value used for fill_mode = "constant"cval=0.,# randomly flip imageshorizontal_flip=True,# randomly flip imagesvertical_flip=False,# set rescaling factor (applied before any other transformation)rescale=None,# set function that will be applied on each inputpreprocessing_function=None,# image data format, either "channels_first" or "channels_last"data_format=None,# fraction of images reserved for validation (strictly between 0 and 1)validation_split=0.0)# Compute quantities required for featurewise normalization# (std, mean, and principal components if ZCA whitening is applied).datagen.fit(x_train)# Fit the model on the batches generated by datagen.flow().model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),validation_data=(x_test, y_test),epochs=epochs, verbose=1, workers=4,callbacks=callbacks)
#保存模型
model.save(filepath)# Score trained model.
scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

懒得解释代码,自行体会吧。最后会生成一个名为cifar10_ResNet20v1_model.h5的模型。
这段代码中,会自动尝试使用GPU进行训练,如果不支持,则使用CPU进行训练,不过那样会非常的慢。我可能会在另一篇文章中,简单记录怎么在ubuntu下安装,英伟达GPU+cudnn+cuda这样的黄金组合,现在暂时不说了。

(二)处理图片(即生成对抗图片)

处理图片我还是使用inception_v3.ckpt这个模型,因为他生成的图片像素较大。这一段内容,我加了较多的注释,可以自行阅读。

import tensorflow as tf
import tensorflow.contrib.slim as slim
import tensorflow.contrib.slim.nets as nets
import PIL
import numpy as np
import tempfile
from urllib.request import urlretrieve
import tarfile
import os
from PIL import Image
import json
import matplotlib.pyplot as plt
import matplotlib.image as mp#首先,设置输入图像。使用tf.Variable而不是使用tf.placeholder,这是因为要确保它是可训练的。当我们需要时,仍然可以输入它。
tf.logging.set_verbosity(tf.logging.ERROR)
sess = tf.InteractiveSession()
image = tf.Variable(tf.zeros((299, 299, 3)))#加载Inception v3模型
def inception(image, reuse):#multiply矩阵对应位置相乘,subtract矩阵对应位置相减,expand_dims,为0时,转变为一维函数preprocessed = tf.multiply(tf.subtract(tf.expand_dims(image, 0), 0.5), 2.0)#weight_decay衰减权重arg_scope = nets.inception.inception_v3_arg_scope(weight_decay=0.0)#arg_scope常用于为tensorflow里的layer函数提供默认值,以使构建模型的代码更加紧凑苗条(slim)# 定义inception-v3模型结构 inception_v3.ckpt里只有参数的取值with slim.arg_scope(arg_scope):# logits  inception_v3前向传播得到的结果logits, _ = nets.inception.inception_v3(preprocessed, 1001, is_training=False, reuse=reuse)logits = logits[:, 1:]  # ignore background class#Softmax简单的说就是把一个N*1的向量归一化为(0,1)之间的值,归一化probs = tf.nn.softmax(logits)  # probabilitiesreturn logits, probslogits, probs = inception(image, reuse=False)#加载预训练的权重
data_dir = './saved_models'
# inception_tarball, _ = urlretrieve(
#     'http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz')
# tarfile.open(inception_tarball, 'r:gz').extractall(data_dir)
restore_vars = [var for var in tf.global_variables()#startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的,根据判断结果返回 true 或 falseif var.name.startswith('InceptionV3/')
]#创建一个saver
saver = tf.train.Saver(restore_vars)
#恢复模型
saver.restore(sess, os.path.join(data_dir, 'inception_v3.ckpt'))#显示图像,并对它进行分类及显示分类结果
#是Imagenet图像的类别标注json文件
imagenet_json, _ = urlretrieve('http://www.anishathalye.com/media/2017/07/25/imagenet.json')
with open(imagenet_json) as f:imagenet_labels = json.load(f)#创建显示界面
def classify(img, correct_class=None, target_class=None, label='o'):fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 8))fig.sca(ax1)p = sess.run(probs, feed_dict={image: img})[0]ax1.imshow(img)fig.sca(ax1)topk = list(p.argsort()[-10:][::-1])topprobs = p[topk]print(topprobs)barlist = ax2.bar(range(10), topprobs)for t in topk:print(topk.index(t))barlist[topk.index(t)].set_color('r')for i in topk:print(topk.index(i))barlist[topk.index(i)].set_color('g')plt.sca(ax2)plt.ylim([0, 1.1])plt.xticks(range(10),[imagenet_labels[i][:15] for i in topk],rotation='vertical')fig.subplots_adjust(bottom=0.2)plt.show()#加载图像,并确保它已被正确分类
img_path = './picture/test_adv.jpg'
#图片类型
# img_class = 388  # “大熊猫 giant panda”
img = PIL.Image.open(img_path)
#获取宽度和高度之间的最大值
big_dim = max(img.width, img.height)
#判断宽度是否大于高度
wide = img.width > img.height
#如果wide是false
new_w = 299 if not wide else int(img.width * 299 / img.height)
#如果wide是true
new_h = 299 if wide else int(img.height * 299 / img.width)
#重新设置尺寸尺寸,长宽最大值位299
img = img.resize((new_w, new_h)).crop((0, 0, 299, 299))
#归一化
img = (np.asarray(img) / 255.0).astype(np.float32)
classify(img)
# classify(img, correct_class=img_class, label='o')#编写一个TensorFlow op进行相应的初始化
x = tf.placeholder(tf.float32, (299, 299, 3))
#输入可训练的对抗样本
x_hat = image  # our trainable adversarial input
#赋值,给x_hat赋x值
assign_op = tf.assign(x_hat, x)#编写梯度下降步骤以最大化目标类的对数概率
#生成4字节数组
learning_rate = tf.placeholder(tf.float32, ())
#生成4字节数组
y_hat = tf.placeholder(tf.int32, ())
#将标签信息转换成one_hot格式,方便评价
labels = tf.one_hot(y_hat, 1000)
#求取输出属于某一类的概率,衡量各个概率分布之间的相似性
loss = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=[labels])
#实现实现梯度下降算法的优化器类
optim_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, var_list=[x_hat])#编写投影步骤,使得对抗样本在视觉上与原始图像相似。另外,将其限定为[0,1]范围内保持有效的图像
epsilon = tf.placeholder(tf.float32, ())below = x - epsilon
above = x + epsilon
projected = tf.clip_by_value(tf.clip_by_value(x_hat, below, above), 0, 1)
with tf.control_dependencies([projected]):project_step = tf.assign(x_hat, projected)#准备合成一个对抗样本。我们任意选择长臂猿作为我们的目标类
demo_epsilon = 2.0 / 255.0  # a really small perturbation
demo_lr = 1e-2
#收敛次数
demo_steps = 100
#在数据集中的目标类的标签号,即长臂猿的标签号
demo_target = 368  # "长臂猿gibbon"# 初始化
# sess.run(assign_op, feed_dict={x: img})
sess.run(assign_op, feed_dict={x: img})#使用img替换掉x的输出结果,所以打印出来img的结果# projected gradient descent
for i in range(demo_steps):# 梯度下降_, loss_value = sess.run([optim_step, loss],feed_dict={learning_rate: demo_lr, y_hat: demo_target})# project stepsess.run(project_step, feed_dict={x: img, epsilon: demo_epsilon})if (i + 1) % 10 == 0:print('step %d, loss=%g' % (i + 1, loss_value))adv = x_hat.eval()  # retrieve the adversarial example
# import cv2
# adv=cv2.resize(adv,(800,800))#大图为200*200
# img_r=800-adv.shape[0]#第0个维度填充到200需要的像素点个数
# img_b=800-adv.shape[1]#第1个维度填充到200需要的像素点个数
# img_pad=np.pad(adv,((0,img_r),(0,img_b),(0,0)),'constant', constant_values=0)mp.imsave('./picture/test_adv.jpg', adv)
classify(adv)

我把利用cifar10模型进行图片生成的代码也贴在这里,如果需要,自行取用,这一段最重要的,是用到了IBM的art工具包,有调用那几个算法的方法。

from os.path import abspath
import sys
import os
import tensorflow as tfsys.path.append(abspath('.'))import keras
import numpy as np
import pickleimport matplotlib.pyplot as plt
plt.show()from keras.datasets import cifar10
from keras.models import load_model
from keras.utils import to_categorical
from imageio import imread
from PIL import Imagefrom art.classifiers import KerasClassifier
from art.attacks.evasion import FastGradientMethod
from art.attacks.evasion import BasicIterativeMethod
from art.attacks.evasion import SaliencyMapMethod
from art.attacks.evasion import DeepFool
#输入图片的路径
input_dir = "./picture"
#输出图片的路径
output_dir = "./out"#生成数组的宽度维度
image_width = 32
#生成数组的高度维度
image_height = 32
#批量训练样本的数量
batch_size = 10#设置数组
#(样本数,行或称为高,列或称为宽,通道数)
batch_shape = [batch_size, image_height, image_width, 3]#加载图片
def load_images(input_dir, batch_shape,Model):#全填0images = np.zeros(batch_shape)filenames = []idx = 0batch_size = batch_shape[0]for filepath in sorted(tf.gfile.Glob(os.path.join(input_dir, '*.png'))):with tf.gfile.Open(filepath, "rb") as f:#归一化处理,两种方法,一种是除以255,值在[0,1]之间,一种是除以127.5-1,值在[-1,1]之间images[idx, :, :, :] = imread(f, pilmode='RGB').astype('float32')/255.0# images[idx, :, :, :] = imread(f, pilmode='RGB').astype(np.float) * 2.0 / 255.0 - 1.0filenames.append(os.path.basename(filepath))idx += 1if idx == batch_size:yield filenames, images,idxfilenames = []images = np.zeros(batch_shape)idx = 0if idx > 0:yield filenames, images,idx#输出图片
def save_images(images, filenames, output_dir):for i, filename in enumerate(filenames):# Images for inception classifier are normalized to be in [-1, 1] interval,# so rescale them back to [0, 1].with tf.gfile.Open(os.path.join(output_dir, filename), 'w') as f:img = (images[i, :, :, :] * 255.0).astype(np.uint8)# img = (((images[i, :, :, :] + 1.0) * 0.5) * 255.0).astype(np.uint8)Image.fromarray(img).save(f, format='png')#GPU内存不足,对GPU进行按需分配
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
keras.backend.tensorflow_backend.set_session(tf.Session(config=config))#加载模型
my_model = load_model('./saved_models/cifar10_ResNet20v1_model.h5')# 加载需要处理的图片
image_iterator = load_images(input_dir, batch_shape,my_model)# 得到第一个batch的图片
filenames, images ,idx= next(image_iterator)#根据模型,生成分类器
classifier = KerasClassifier( model=my_model)# Craft adversarial samples with FGSM,加扰动
epsilon = 0.03  # Maximum perturbation
adv_fgsm_crafter = FastGradientMethod(classifier)
x_test_adv_fgsm = adv_fgsm_crafter.generate(x=images, eps=epsilon)# Craft adversarial samples with IGSM
# epsilon = 0.015  # Maximum perturbation
# stepsize = 0.005
# adv_igsm_crafter = BasicIterativeMethod(classifier, eps=epsilon, eps_step=stepsize)
# x_test_adv_igsm = adv_igsm_crafter.generate(x=images)#显示干扰前后的图片
fig = plt.figure(figsize=(idx, 2))
columns = idx
rows = 2
for i in range(0, idx):img = images[i].reshape(32, 32, 3)fig.add_subplot(rows, columns, i+1)plt.imshow(img)plt.axis('off')#判断类别y_pred = my_model.predict(images[i].reshape(1, 32, 32, 3))print(np.argmax(y_pred), end=' ')for i in range(0, idx):img_adv = x_test_adv_fgsm[i].reshape(32, 32, 3)fig.add_subplot(rows, columns, i+idx+1)plt.imshow(img_adv)plt.axis('off')# 判断类别y_pred = my_model.predict(x_test_adv_fgsm[i].reshape(1, 32, 32, 3))print(np.argmax(y_pred), end=' ')plt.show()#保存为图片
save_images(x_test_adv_fgsm, filenames, output_dir)

(三)还图片至原像素大小

如果你认真读了图片加扰动的过程,你会发现,不管是哪种方法,第一步都是先把你输入的图片的像素进行resize,即调整了大小,至指定的大小,因此,图片处理完以后,像素也就变小了,cifar10更是只剩下了3232的像素,inception_v3还有299299的像素,但是也不够哈,如果我输入的原图像是800*800的,我可能就得尝试把原图像的像素进行扩充了,当然不能直接填充像素,那样图像会很糟糕,这里我尝试了几种方法,最邻近插值法、双线性插值法、双三次插值法,可以自行看效果决定。

from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import math#最邻近插值法
def NN_interpolation(img, dstH, dstW):scrH, scrW, _ = img.shaperetimg = np.zeros((dstH, dstW, 3), dtype=np.uint8)for i in range(dstH):for j in range(dstW):scrx = round((i + 1) * (scrH / dstH))scry = round((j + 1) * (scrW / dstW))retimg[i, j] = img[scrx - 1, scry - 1]return retimg#双线性插值法
def BiLinear_interpolation(img, dstH, dstW):scrH, scrW, _ = img.shapeimg = np.pad(img, ((0, 1), (0, 1), (0, 0)), 'constant')retimg = np.zeros((dstH, dstW, 3), dtype=np.uint8)for i in range(dstH):for j in range(dstW):scrx = (i + 1) * (scrH / dstH) - 1scry = (j + 1) * (scrW / dstW) - 1x = math.floor(scrx)y = math.floor(scry)u = scrx - xv = scry - yretimg[i, j] = (1 - u) * (1 - v) * img[x, y] + u * (1 - v) * img[x + 1, y] + (1 - u) * v * img[x, y + 1] + u * v * img[x + 1, y + 1]return retimgdef BiBubic(x):x = abs(x)if x <= 1:return 1 - 2 * (x ** 2) + (x ** 3)elif x < 2:return 4 - 8 * x + 5 * (x ** 2) - (x ** 3)else:return 0#双三次插值法
def BiCubic_interpolation(img, dstH, dstW):scrH, scrW, _ = img.shape# img=np.pad(img,((1,3),(1,3),(0,0)),'constant')retimg = np.zeros((dstH, dstW, 3), dtype=np.uint8)for i in range(dstH):for j in range(dstW):scrx = i * (scrH / dstH)scry = j * (scrW / dstW)x = math.floor(scrx)y = math.floor(scry)u = scrx - xv = scry - ytmp = 0for ii in range(-1, 2):for jj in range(-1, 2):if x + ii < 0 or y + jj < 0 or x + ii >= scrH or y + jj >= scrW:continuetmp += img[x + ii, y + jj] * BiBubic(ii - u) * BiBubic(jj - v)retimg[i, j] = np.clip(tmp, 0, 255)return retimgim_path = './picture/test_adv.jpg'
image = np.array(Image.open(im_path))image1 = NN_interpolation(image, 800, 800)
image1 = Image.fromarray(image1.astype('uint8')).convert('RGB')
image1.save('./picture/test_NN.jpg')image2 = BiLinear_interpolation(image, 800, 800)
image2 = Image.fromarray(image2.astype('uint8')).convert('RGB')
image2.save('./picture/test_BiLinear.jpg')image3 = BiCubic_interpolation(image, 800, 800)
image3 = Image.fromarray(image3.astype('uint8')).convert('RGB')
image3.save('./picture/test_BiCubic.jpg')

(四)进一步恢复目视效果(可能会适得其反)

其实这一步,包括上一步,我已经心里没底了,这样的做法的后果,会不会导致之前的加扰动就失效了?不好说,我没法验证。
因此,我先声明,我写这篇文章,只是记录,不保证方法的完全正确,只是给大家留个参考。
我进一步恢复目视效果的方法,就是图像叠加。如果你信不过这一步,或者这一步确实导致图片加扰动失效了,您可以只做到第三步。话不多说,上代码。

from PIL import Image
import math
import matplotlib.pyplot as pltimg = Image.open('./picture/test.jpg')#图片1
img_NN = Image.open('./picture/test_BiLinear.jpg')#图片2
img_BiLinear = Image.open('./picture/test_BiLinear.jpg')#图片2
img_BiCubic = Image.open('./picture/test_BiCubic.jpg')#图片2#该函数的作用是由于 Image.blend()函数只能对像素大小一样的图片进行重叠,故需要对图片进行剪切。
def cut_img(img, x, y):"""函数功能:进行图片裁剪(从中心点出发):param img: 要裁剪的图片:param x: 需要裁剪的宽度:param y: 需要裁剪的高:return: 返回裁剪后的图片"""x_center = img.size[0] / 2y_center = img.size[1] / 2new_x1 = x_center - x//2new_y1 = y_center - y//2new_x2 = x_center + x//2new_y2 = y_center + y//2new_img = img.crop((new_x1, new_y1, new_x2, new_y2))return new_img#print(img1.size, img2.size)# #取两张图片中最小的图片的像素
# new_x = min(img1.size, img_NN.size)[0]
# new_y = min(img1.size, img_NN.size)[1]
#
# new_img1 = cut_img(img1, new_x, new_y)
# new_img2 = cut_img(img_NN, new_x, new_y)
#print(new_img1.size, new_img2.size)#进行图片重叠  最后一个参数是图片的权值
final_img_NN = Image.blend(img, img_NN, (math.sqrt(5)-1)/2)
final_img_BiLinear = Image.blend(img, img_BiLinear, (math.sqrt(5)-1)/2)
final_img_BiCubic = Image.blend(img, img_BiCubic, (math.sqrt(5)-1)/2)
#别问我为什么是  (math.sqrt(5)-1)/2   这个是黄金比例,哈哈!!final_img_NN.save('./picture/test_NN_blend.jpg')
final_img_BiLinear.save('./picture/test_BiLinear_blend.jpg')
final_img_BiCubic.save('./picture/test_BiCubic_blend.jpg')# final_img_NN.show()# fig = plt.figure(figsize=(4, 1))
# columns = 4
# rows = 1
# fig.add_subplot(rows, columns, 1)
# plt.imshow(img)
# plt.axis('off')
#
# fig.add_subplot(rows, columns, 2)
# plt.imshow(final_img_NN)
# plt.axis('off')
#
# fig.add_subplot(rows, columns, 3)
# plt.imshow(final_img_BiLinear)
# plt.axis('off')
#
# fig.add_subplot(rows, columns, 4)
# plt.imshow(final_img_BiCubic)
# plt.axis('off')# plt.show()

三、总结

后面的代码不重要,重要的是第一部分的解释,希望给大家能够提个醒,当然,那些解释只能作为我的理解,不保证正确,欢迎批评指正。

四、附件下载

附件:
cifar-10-batches-py.tar.gz下载链接
本文的代码链接(包含训练好的两个模型)

利用python实现深度学习生成对抗样本模型,为任一图片加扰动并恢复原像素的全流程记录相关推荐

  1. 【深度学习】深度学习之对抗样本问题和知识蒸馏技术

    文章目录 1 什么是深度学习对抗样本 2 深度学习对于对抗样本表现的脆弱性产生的原因 3 深度学习的对抗训练 4 深度学习中的对抗攻击和对抗防御 5 知识蒸馏技术5.1 知识蒸馏介绍5.2 为什么要有 ...

  2. 深度学习生成对抗网络(GAN)

    一.概述 生成对抗网络(Generative Adversarial Networks)是一种无监督深度学习模型,用来通过计算机生成数据,由Ian J. Goodfellow等人于2014年提出.模型 ...

  3. 你真的了解深度学习生成对抗网络(GAN)吗?

    生成对抗网络(GANs,https://en.wikipedia.org/wiki/Generative_adversarial_network)是一类具有基于网络本身即可以生成数据能力的神经网络结构 ...

  4. [深度学习]生成对抗网络的实践例子

    系列文章目录 深度学习GAN(一)之简单介绍 深度学习GAN(二)之DCGAN基于CIFAR10数据集的例子 深度学习GAN(三)之DCGAN基于手写体Mnist数据集的例子 深度学习GAN(四)之c ...

  5. Python基于深度学习多标签分类模型实现云状识别

    其实这个比赛早在19年的时候就结束,比赛名为<Understanding Clouds from Satellite Images>,原来的任务其实不仅要识别出来类型还要能够分割出来具体的 ...

  6. ACL2020 | 使用强化学习为机器翻译生成对抗样本

    2020-07-12 03:08:49 本文介绍的是 ACL 2020 论文<A Reinforced Generation of Adversarial Examples for Neural ...

  7. 对抗攻击之利用水印生成对抗样本

    本文为52CV粉丝鬼道投稿,介绍了对抗学习领域最新的工作Adv-watermark. 论文标题:Adv-watermark: A Novel Watermark Perturbation for Ad ...

  8. Python Web 深度学习实用指南:第一、二部分

    原文:Hands-On Python Deep Learning for the Web 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自[ApacheCN 深度学习 译文集],采用译后编辑 ...

  9. 深度卷积生成对抗网络

    深度卷积生成对抗网络 Deep Convolutional Generative Adversarial Networks GANs如何工作的基本思想.可以从一些简单的,易于抽样的分布,如均匀分布或正 ...

最新文章

  1. linux内核开机显示企鹅logo,批改linux内核kernel开机logo(小企鹅)
  2. python画图-用Python画图
  3. Minetorch教程
  4. 在package-lock.json中指定node-mass版本+独立编译flink中的flink-runtime-web模块
  5. 第一章:线性空间和线性变换
  6. [vue] vue的is这个特性你有用过吗?主要用在哪些方面?
  7. java 实现不同用户编辑 word 文档的不同区域
  8. python计算机中丢失api-ms-win-crt-runtime-l_api-ms-win-crt-runtime-l1-1-0.dll
  9. springBoot中shiro与Redis整合的配置文件
  10. POJ2536 Gopher II【二分图最大匹配】
  11. 【软件工程导论】期末复习试题集
  12. 【UML】UML基础教程(总三篇文章)
  13. linux中telnet命令安装包,linux的telnet命令安装
  14. 2. 测度论-分布与分布函数
  15. 微信小程序引入外部icon
  16. 富文本编辑器抓取秀米图片转存到七牛云
  17. 软件测试教学实训平台
  18. android 铃音制作工具,来电铃声大全制作软件
  19. C++ sqlite3解决中文排序问题
  20. 简体-繁体互转换的一个JS

热门文章

  1. php培训总结如何写,济南php培训学员的学习笔记总结
  2. 凡科虚拟服务器怎样做301,虚拟主机如何做301重定向
  3. 推广都需要哪些工具?这几款工具可以轻松提高推广效率
  4. 最先进的智能采茶机器人_一种智能采茶机器人的制作方法
  5. 真实的玻璃反光效果PS图层样式
  6. 2022中国制造业行业研究报告:(现状、驱动因素、外流现象、发展趋势)27页可下载
  7. 十九 Java的语言基础(System类 Date类)
  8. Docker 从入门到实践系列五 - Dockerfile文件
  9. jmeter断言(自动判断实际和预期结果是否相符的jmeter组件)
  10. 小程序数据分析(有数)