Re snet -50 состязательная тренировка с умением FGSM от умных людей, застрявшая на 5% - PullRequest
1 голос
/ 07 апреля 2020

Я сталкиваюсь со странной проблемой, когда тренирую re snet -50, и не уверен, является ли это логической ошибкой или ошибкой где-то в коде / библиотеках. Я провожу тренировку re snet -50, загруженного из Keras, используя FastGradientMethod от cleverhans и ожидаю, что точность состязательности возрастет как минимум выше 90% (вероятно, 99.x%). Алгоритм обучения, параметры обучения и атаки должны быть видны в коде. Проблема, как уже указывалось в заголовке, состоит в том, что точность остается на уровне 5% после обучения ~ 3000 из 39002 входов обучения в первой эпохе. (GermanTrafficSignRecognitionBenchmark, GTSRB ).

При замене сети на al enet -5, ale xnet и vgg19 код работает должным образом, и его точность абсолютно сопоставима с не состязательная, категориальная функция потери корсентропии достигается. Я также пытался запустить процедуру, используя только tf-cpu и различные версии tenorflow, результат всегда один и тот же.

Код для получения Re sNet -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 керас: 2.3.1 умные люди:> 3.0.1 - последний версия вытащила из github

1 Ответ

0 голосов
/ 07 мая 2020

Это побочный эффект от того, как мы оцениваем скользящие средние при нормализации партии.

Среднее значение и дисперсия обучающих данных, которые вы использовали, отличаются от данных из набора данных, используемого для обучения ResNet50 , Поскольку импульс для BatchNormalization имеет значение по умолчанию, равное 0,99, при наличии всего 10 итераций он не достаточно быстро сходится к правильным значениям для скользящего среднего и дисперсии. Это не очевидно во время обучения, когда learning_phase равен 1, потому что BN использует среднее значение / дисперсию пакета. Тем не менее, когда мы устанавливаем learning_phase в 0, неверные средние / дисперсионные значения, которые выучены во время обучения, значительно влияют на точность.

Эту проблему можно решить с помощью следующих подходов:

  1. Больше итераций

Уменьшите размер пакета с 32 до 16 (чтобы выполнить больше обновлений) за эпоху) и увеличьте количество эпох с 10 до 250. Таким образом, скользящая средняя и дисперсия будут сходиться к правильным значениям.

Изменение импульса BatchNormalization

Сохраняйте фиксированное количество итераций, но изменяйте импульс слоя BatchNormalization, чтобы более агрессивно обновлять скользящее среднее и дисперсию (не рекомендуется для производственных моделей).

В исходном фрагменте добавьте следующий код между чтением base_model и определением новых слоев:

# ....
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
# ....

Также рекомендуем вам попробовать другой взлом при условии, что нас здесь .

...