Я пытаюсь реализовать эволюционный алгоритм, используя эволюционную стратегию MAP-Elites и оператор полиномиальной мутации (как определено здесь в пункте 2.), как обсуждалось в этой статье. Я создал три разные модели MNIST и обучил их, используя tenorflow == 2.0.0a, используя API Keras Модели работают нормально (точность около 95%).
Мое понимание упомянутой эволюционной стратегии состоит в том, что мы создаем начальную популяцию, а затем с каждой итерацией мутируем случайным образом выбранный образец из этой популяции. Если после мутации новый образец получает уверенность в принадлежности к какому-либо классу выше, чем ранее выбранный лучший образец для этого класса, тогда мы рассматриваем его как лучший и добавляем его в популяцию. Ожидаемый результат состоит в том, что после завершения алгоритма у нас должен быть образец для каждого из классов, который удается классифицировать в этом классе с высокой степенью достоверности. Первоначальная совокупность изображений создается с использованием равномерного распределения.
Проблема в том, что мои модели классифицируют случайный ввод, созданный с равномерным распределением, всегда как один и тот же класс с высокой степенью достоверности (то есть модель CNN всегда классифицирует его как 8). Таким образом, большинство или все образцы, с которыми я получаю доступ, классифицируются как один и тот же класс (с незначительно изменяющимися степенями принадлежности к другим классам) даже после большой начальной популяции и числа итераций (т.е. 1000 исходных образцов и 20000 итераций).
Входные выборки нормированы на диапазон [0.0, 1.0]. Все рассуждения, приведенные ниже, ограничены для упрощения только плотной моделью (CNN и упрощенный LeNet5, дающей аналогичные результаты), описанной внизу.
Использование нормального распределения со средним значением = 0,0 и стандартным значением = 0,3 или средним значением = 0,5 и стандартным стандартным значением = 0,3 для создания начальной популяции и вероятности мутации 0,3 (вместо 0,1, как в статье) дает аналогичные результаты.
Я пытался использовать (1, λ) эволюционную стратегию с таргетингом только на один класс (начальная популяция 100, 100 поколений), и она дает лучшие результаты, чем реализованная ниже MAP-Elites (я могу сгенерировать образец для более чем одного класса).
Я пытался не нормализовать данные для модели и снова тренировать их, используя диапазон [0, 255], но результаты были почти такими же. Я также попытался использовать гауссовский оператор мутации вместо полинома, но это, похоже, не имело большого значения.
Отключение увеличения данных, когда тренировка, кажется, не дает эффекта.
Вот реализация, которую я написал.
def evolutionary_attack(model, population_size, generations_nmb, min=0.0, max=1.0, mutation_chance=0.1, mutation_power=15):
population = [] #
best_in_class = {} #dictionary of specimen performing best for given class
for x in range(population_size):
population.append(np.random.uniform(min, max, model.get_input_shape())) #initialize specimens with random values
# population.append(np.random.normal(0.0, 0.3, model.get_input_shape())) #initialize specimens with random values
for i in range(generations_nmb):
current_specimen = random.choice(population) #choose specimen at random from the population
mutated_specimen = mutate_specimen(current_specimen, min, max, mutation_chance, mutation_power)
logits = model(tf.expand_dims(tf.convert_to_tensor(mutated_specimen, dtype=tf.float32), 0))
certainties_per_class = tf.squeeze(logits).numpy()
for cur_class in range(len(certainties_per_class)):
if cur_class in best_in_class:
_, best_certainty = best_in_class[cur_class]
if certainties_per_class[cur_class] > best_certainty:
#if the new specimen performs better in given class make it a new best and add it to the population
best_in_class[cur_class] = (mutated_specimen, certainties_per_class[cur_class])
population.append(mutated_specimen)
else:
best_in_class[cur_class] = (mutated_specimen, certainties_per_class[cur_class]) #handles the case when there is no best specimen for the given class
def mutate_specimen(specimen, min, max, mutation_chance, mutation_power=15):
specimen = np.copy(specimen)
with np.nditer(specimen, op_flags=['readwrite']) as it:
for old_val in it:
if np.random.uniform() < mutation_chance:
u = np.random.uniform()
if u <= 0.5:
delta = ((2.0 * u) ** (1.0 / (mutation_power))) - 1.0
new_val = old_val + delta * (old_val - min)
else:
delta = 1.0 - (2 * (1 - u))** (1 / (1 + mutation_power))
new_val = old_val + delta * (max - old_val)
old_val[...] = new_val
return np.clip(specimen, min, max)
В статье авторы утверждают, что им удалось получить образцы для каждой цифры, классифицированные с достоверностью> 99,99% после 50 поколений. Это сильно отличается от результатов, которые я получаю. Кажется, что я делаю что-то не так, но я не могу точно определить проблему. Я не уверен, что это просто небольшая ошибка в коде или мои рассуждения о реализации неверны.
Моя модель сконструирована так
DenseModel (функция активации сигмоида на всех слоях, кроме последнего)
input_1 (InputLayer) [(None, 28, 28, 1)] 0
flatten (Flatten) (Нет, 784) 0
плотный (плотный) (нет, 784) 615440
density_1 (Плотный) (Нет, 800) 628000
density_2 (Плотный) (Нет, 800) 640800
плотность_3 (Плотный) (нет, 10) 8010
Он был обучен для нескольких эпох с увеличением данных с помощью оптимизатора Адама.
РЕДАКТИРОВАТЬ: я просто заметил, что я не обрезать значения образцов после мутации. Если я сделаю это, то использование нормального распределения даст результаты, аналогичные использованию равномерного распределения. я исправил это в опубликованном коде. глупая ошибка