Tensorflow 使用克里夫汉斯FGSM的Resnet-50对抗性训练精度保持在5%

Tensorflow 使用克里夫汉斯FGSM的Resnet-50对抗性训练精度保持在5%,tensorflow,keras,cleverhans,Tensorflow,Keras,Cleverhans,在对抗性地训练resnet-50时,我面临一个奇怪的问题,我不确定这是一个逻辑错误,还是代码/库中的某个错误。 我正在使用克利夫汉斯的FastGradientMethod,对抗性地训练一架从Keras装载的resnet-50,并期望对抗精度至少提高到90%以上(可能是99.x%)。训练算法、训练参数和攻击参数应在代码中可见。 正如标题中所述,问题在于,在第一个历元中训练39002个训练输入中的3000个训练输入后,精确度仍停留在5%。(德国官方认可基准,) 在没有和敌方损失函数的情况下进行训练时

在对抗性地训练resnet-50时,我面临一个奇怪的问题,我不确定这是一个逻辑错误,还是代码/库中的某个错误。 我正在使用克利夫汉斯的FastGradientMethod,对抗性地训练一架从Keras装载的resnet-50,并期望对抗精度至少提高到90%以上(可能是99.x%)。训练算法、训练参数和攻击参数应在代码中可见。 正如标题中所述,问题在于,在第一个历元中训练39002个训练输入中的3000个训练输入后,精确度仍停留在5%。(德国官方认可基准,)

在没有和敌方损失函数的情况下进行训练时,在3000个样本后,精度不会停滞,但在第一个历元中会继续提高>0.95

当使用lenet-5、alexnet和vgg19替换网络时,代码按预期工作,并且实现了与非对抗性、分类的企业中心损失功能绝对可比的准确性。我也尝试过使用tf cpu和不同版本的tensorflow来运行这个过程,结果总是一样的

获取ResNet-50的代码:

def build_resnet50(num_classes, img_size):
    from tensorflow.keras.applications import ResNet50
    from tensorflow.keras import Model
    from tensorflow.keras.layers import Dense, Flatten
    resnet = ResNet50(weights='imagenet', include_top=False, input_shape=img_size)
    x = Flatten(input_shape=resnet.output.shape)(resnet.output)
    x = Dense(1024, activation='sigmoid')(x)
    predictions = Dense(num_classes, activation='softmax', name='pred')(x)
    model = Model(inputs=[resnet.input], outputs=[predictions])
    return model
培训:

def lr_schedule(epoch):
    # decreasing learning rate depending on epoch
    return 0.001 * (0.1 ** int(epoch / 10))


def train_model(model, xtrain, ytrain, xtest, ytest, lr=0.001, batch_size=32, 
epochs=10, result_folder=""):
    from cleverhans.attacks import FastGradientMethod
    from cleverhans.utils_keras import KerasModelWrapper
    import tensorflow as tf

    from tensorflow.keras.optimizers import SGD
    from tensorflow.keras.callbacks import LearningRateScheduler, ModelCheckpoint
    sgd = SGD(lr=lr, decay=1e-6, momentum=0.9, nesterov=True)

    model(model.input)

    wrap = KerasModelWrapper(model)
    sess = tf.compat.v1.keras.backend.get_session()
    fgsm = FastGradientMethod(wrap, sess=sess)
    fgsm_params = {'eps': 0.01,
                   'clip_min': 0.,
                   'clip_max': 1.}

    loss = get_adversarial_loss(model, fgsm, fgsm_params)

    model.compile(loss=loss, optimizer=sgd, metrics=['accuracy'])

    model.fit(xtrain, ytrain,
                    batch_size=batch_size,
                    validation_data=(xtest, ytest),
                    epochs=epochs,
                    callbacks=[LearningRateScheduler(lr_schedule)])
损失函数:

def get_adversarial_loss(model, fgsm, fgsm_params):
    def adv_loss(y, preds):
         import tensorflow as tf

        tf.keras.backend.set_learning_phase(False) #turn off dropout during input gradient calculation, to avoid unconnected gradients

        # Cross-entropy on the legitimate examples
        cross_ent = tf.keras.losses.categorical_crossentropy(y, preds)

        # Generate adversarial examples
        x_adv = fgsm.generate(model.input, **fgsm_params)
        # Consider the attack to be constant
        x_adv = tf.stop_gradient(x_adv)

        # Cross-entropy on the adversarial examples
        preds_adv = model(x_adv)
        cross_ent_adv = tf.keras.losses.categorical_crossentropy(y, preds_adv)

        tf.keras.backend.set_learning_phase(True) #turn back on

        return 0.5 * cross_ent + 0.5 * cross_ent_adv
    return adv_loss
使用的版本: tf+tf gpu:1.14.0 keras:2.3.1
cleverhans:>3.0.1-从github推出的最新版本

这是我们估计批次标准化移动平均值方法的副作用

您使用的训练数据的均值和方差与用于训练ResNet50的数据集的均值和方差不同。由于BatchNormalization上的动量的默认值为0.99,仅进行10次迭代,其收敛速度不足以达到移动平均值和方差的正确值。当学习阶段为1时,这在训练期间并不明显,因为BN使用批次的均值/方差。然而,当我们将learning_phase设置为0时,在训练期间学习到的不正确的均值/方差值会显著影响准确性

您可以通过以下方法解决此问题:

  • 更多迭代
  • 将批处理的大小从32减少到16(以便每个历元执行更多更新),并将历元的数量从10增加到250。这样,移动平均值和方差将收敛到正确的值

  • 改变正常化的势头
  • 保持迭代次数固定,但更改BatchNormalization层的动量,以更积极地更新滚动平均值和方差(不建议用于生产模型)

    在原始代码段上,在读取基本模型和定义新层之间添加以下代码:

    # ....
    base_model = ResNet50(weights='imagenet', include_top=False, input_shape=input_shape)
    
    # PATCH MOMENTUM - START
    import json
    conf = json.loads(base_model.to_json())
    for l in conf['config']['layers']:
        if l['class_name'] == 'BatchNormalization':
            l['config']['momentum'] = 0.5
    
    
    m = Model.from_config(conf['config'])
    for l in base_model.layers:
        m.get_layer(l.name).set_weights(l.get_weights())
    
    base_model = m
    # PATCH MOMENTUM - END
    
    x = base_model.output
    # ....
    

    还建议您尝试bu us提供的另一种破解方法。

    这是我们估计批次标准化移动平均线方法的副作用

    您使用的训练数据的均值和方差与用于训练ResNet50的数据集的均值和方差不同。由于BatchNormalization上的动量的默认值为0.99,仅进行10次迭代,其收敛速度不足以达到移动平均值和方差的正确值。当学习阶段为1时,这在训练期间并不明显,因为BN使用批次的均值/方差。然而,当我们将learning_phase设置为0时,在训练期间学习到的不正确的均值/方差值会显著影响准确性

    您可以通过以下方法解决此问题:

  • 更多迭代
  • 将批处理的大小从32减少到16(以便每个历元执行更多更新),并将历元的数量从10增加到250。这样,移动平均值和方差将收敛到正确的值

  • 改变正常化的势头
  • 保持迭代次数固定,但更改BatchNormalization层的动量,以更积极地更新滚动平均值和方差(不建议用于生产模型)

    在原始代码段上,在读取基本模型和定义新层之间添加以下代码:

    # ....
    base_model = ResNet50(weights='imagenet', include_top=False, input_shape=input_shape)
    
    # PATCH MOMENTUM - START
    import json
    conf = json.loads(base_model.to_json())
    for l in conf['config']['layers']:
        if l['class_name'] == 'BatchNormalization':
            l['config']['momentum'] = 0.5
    
    
    m = Model.from_config(conf['config'])
    for l in base_model.layers:
        m.get_layer(l.name).set_weights(l.get_weights())
    
    base_model = m
    # PATCH MOMENTUM - END
    
    x = base_model.output
    # ....
    

    我还建议您尝试bu us提供的另一种黑客攻击。

    @PlassMa-如果答案回答了您的问题,请您接受并投票。谢谢。谢谢你的回答,不幸的是,这两个选项都不起作用。。。我认为这不是一个没有足够快的收敛速度的问题,因为非对抗性训练在1个历元后产生>0.95的准确度…@PlassMa-有趣。那么你是说非对抗性Resnet训练的准确率>95%,而对抗性Resnet训练的准确率保持在5%以上?与lenet-5、alexnet和vgg19相同的对抗性训练是否如预期的那样有效?你也尝试过这种方法吗?也可以在google colab文件中共享完整的代码并在此处共享链接吗?@PlassMa-如果答案回答了您的问题,请您接受并投票。谢谢。谢谢你的回答,不幸的是,这两个选项都不起作用。。。我认为这不是一个没有足够快的收敛速度的问题,因为非对抗性训练在1个历元后产生>0.95的准确度…@PlassMa-有趣。那么你是说非对抗性Resnet训练的准确率>95%,而对抗性Resnet训练的准确率保持在5%以上?与lenet-5、alexnet和vgg19相同的对抗性训练是否如预期的那样有效?你也尝试过这种方法吗?也可以在google colab文件中共享完整的代码并在此处共享链接吗?