Привет, Хабр! Наверняка вы заметили, что в ваших сетях активно обсуждается тема стилизации фотографий под различные художественные стили.
Читая все эти популярные статьи, можно подумать, что под капотом этих приложений творится магия, а нейросеть действительно придумывает и перерисовывает изображение с нуля.
Так уж получилось, что перед нашей командой стояла похожая задача: в рамках внутрикорпоративного хакатона мы сделали стилизация видео , т.к.
уже было приложение для фотографий.
В этом посте мы разберемся, как сеть «перерисовывает» изображения, и разберем статьи, благодаря которым это стало возможным.
Я рекомендую проверить последний пост перед прочтением этого материала и вообще с основами сверточные нейронные сети .
Вас ждут несколько формул и немного кода (примеры приведу на Теано И лазанья ), а также много фотографий.
Этот пост организован в хронологическом порядке появления статей и, соответственно, самих идей.
Иногда я буду разбавлять это нашим недавним опытом.
Вот мальчик из ада, который привлечет ваше внимание.
Визуализация и понимание сверточных сетей (28 ноября 2013 г.
) Прежде всего стоит упомянуть статью, в которой авторы смогли показать, что нейросеть – это не черный ящик, а вполне интерпретируемая вещь (кстати, сегодня об этом можно сказать и так).
не только о сверточных сетях для компьютерного зрения).
Авторы решили научиться интерпретировать активацию нейронов скрытых слоев; для этого они использовали деконволюционная нейронная сеть (deconnet), предложенный несколькими годами ранее (кстати, теми же Зайлером и Фергюсом, которые являются авторами данной публикации).
Сеть деконволюции на самом деле представляет собой сеть, в которой свертки и объединение применяются в обратном порядке.
В оригинальной работе над deconnet сеть использовалась в режиме обучения без присмотра для генерации изображений.
На этот раз авторы использовали его просто для возврата от признаков, полученных после прямого прохода через сеть, к исходному изображению.
В результате получается изображение, которое можно интерпретировать как сигнал, вызвавший эту активацию в нейронах.
Естественно, возникает вопрос: как сделать обратный проход через свертку и нелинейность? И уж тем более через max-pooling это уж точно не обратимая операция.
Давайте рассмотрим все три компонента.
Обратный РеЛу
В сверточных сетях часто используется функция активации.ReLu(x) = max(0, x) , что делает все активации на слое неотрицательными.
Соответственно, при обратном обходе нелинейности также необходимо получить неотрицательные результаты.
Для этого авторы предлагают использовать тот же ReLu. С точки зрения архитектуры Theano необходимо переопределить функцию градиента операции ( бесконечно ценный ноутбук в рецепты лазаньи , оттуда вы получите подробную информацию о том, что такое класс ModifiedBackprop).
class ZeilerBackprop(ModifiedBackprop): def grad(self, inputs, out_grads): (inp,) = inputs (grd,) = out_grads #return (grd * (grd > 0).
astype(inp.dtype),) # explicitly rectify return (self.nonlinearity(grd),) # use the given nonlinearity
Обратная свертка
Тут немного сложнее, но всё логично: достаточно применить к выводам обратного ReLu вместо предыдущего слоя, используемого при прямом проходе, транспонированную версию того же ядра свертки, но к выходам обратного ReLu. Но боюсь, что на словах это не так очевидно, давайте посмотрим на визуализацию этой процедуры( здесь вы найдете еще больше визуализаций извилин).Свертка с шагом = 1
Свертка с шагом = 1 | Обратная версия |
---|---|
|
|
Свертка с шагом = 2 | Обратная версия |
---|---|
|
|
Обратное объединение
Эта операция (в отличие от предыдущих), как правило, необратима.Но нам бы все же хотелось как-то пережить максимум при обратном прохождении.
Для этого авторы предлагают использовать карту того, где был максимум при прямом проходе (переключатели положения максимума).
Во время обратного прохода входной сигнал преобразуется в непулируемый таким образом, чтобы приблизительно сохранить структуру исходного сигнала; здесь действительно легче увидеть, чем описать.
Результат
Алгоритм визуализации предельно прост:- Сделайте прямой пас.
- Выбираем интересующий нас слой.
- Запишите активацию одного или нескольких нейронов и обнулите остальные.
- Сделайте противоположный вывод.
Для наглядности нейроны внутри одного слоя сгруппированы в тематические группы.
В общем, вдруг оказалось, что нейросеть узнает именно то, о чем писали Хьюбел и Вайзель.
в работе над строением зрительной системы , за что им была присуждена Нобелевская премия в 1981 году.
Благодаря этой статье мы получили наглядное представление о том, чему обучается сверточная нейронная сеть на каждом слое.
Именно эти знания в дальнейшем позволят манипулировать содержимым сгенерированного изображения, но до этого еще далеко; следующие несколько лет были потрачены на совершенствование методов «трепанирования» нейронных сетей.
Кроме того, авторы статьи предложили способ анализа того, как лучше построить архитектуру сверточной нейронной сети для достижения лучших результатов (правда, они не выиграли ImageNet 2013, но пробились в топ; УПД : оказывается они победили, Кларифаи такие они есть).
Визуализация функций
Вот пример визуализации активаций с помощью deconnet, сегодня этот результат выглядит так себе, но тогда это был прорыв.
Карты значимости с использованием deconnet
Глубоко внутри сверточных сетей: визуализация моделей классификации изображений и карт значимости (19 апреля 2014 г.
) Данная статья посвящена исследованию методов визуализации знаний, содержащихся в сверточной нейронной сети.
Авторы предлагают два метода визуализации, основанные на градиентном спуске.
Визуализация модели класса
Итак, представьте, что у нас есть обученная нейронная сеть для решения задачи классификации на определенное количество классов.
Обозначим через
значение активации выходного нейрона, соответствующего классу с .
Тогда следующая задача оптимизации дает нам именно то изображение, которое максимизирует выбранный класс:
Эту проблему можно легко решить с помощью Theano. Обычно мы просим платформу взять производную по параметрам модели, но на этот раз мы предполагаем, что параметры фиксированы, а производная берется по входному изображению.
Следующая функция выбирает максимальное значение выходного слоя и возвращает функцию, которая вычисляет производную входного изображения.
def compile_saliency_function(net):
"""
Compiles a function to compute the saliency maps and predicted classes
for a given minibatch of input images.
"""
inp = net['input'].
input_var
outp = lasagne.layers.get_output(net['fc8'], deterministic=True)
max_outp = T.max(outp, axis=1)
saliency = theano.grad(max_outp.sum(), wrt=inp)
max_class = T.argmax(outp, axis=1)
return theano.function([inp], [saliency, max_class])
Вы наверняка видели в Интернете странные изображения с собачьими мордами — DeepDream. В оригинальной статье авторы используют следующий процесс для создания изображений, максимизирующих выбранный класс:
- Инициализируйте исходное изображение нулями.
- Рассчитайте производное значение по этому изображению.
- Измените изображение, добавив к нему полученное изображение из производной.
- Вернитесь к точке 2 или выйдите из цикла.
Что, если мы инициализируем первое изображение реальной фотографией и запустим тот же процесс? Но на каждой итерации мы будем выбирать случайный класс, обнулять остальное и вычислять значение производной, тогда у нас получится что-то вроде этого глубокого сна.
Внимание 60 мб
Почему здесь так много собачьих морд и глаз? Все просто: в имидженете почти 200 собак из 1000 классов, у них есть глаза.
А также много занятий, где есть просто люди.
Извлечение значимости классов Если этот процесс инициализировать с реальной фотографией, остановить после первой итерации и нарисовать значение производной, то мы получим такое изображение, добавив которое к исходному, мы увеличим значение активации выбранного класса.
Карты значимости с использованием производной
И снова результат «так себе».
Важно отметить, что это новый способ визуализации активаций (ничто не мешает нам фиксировать значения активации не на последнем слое, а на любом слое сети вообще и брать производную относительно входного изображения ).
Следующая статья объединит оба предыдущих подхода и предоставит нам инструмент для настройки переноса стилей, который будет описан позже.
Стремление к простоте: вся сверточная сеть (13 апреля 2015 г.
) Эта статья вообще не о визуализации, а о том, что замена пулинга на свертку с большим шагом не приводит к потере качества.
Но в качестве побочного продукта своего исследования авторы предложили новый способ визуализации функций, который они использовали для более точного анализа того, что изучает модель.
Их идея такова: если мы просто берем производную, то при деконволюции те признаки, которые во входном изображении были меньше нуля, не передаются обратно (используя ReLu для входного изображения).
А это приводит к появлению отрицательных значений на распространяемом обратно изображении.
С другой стороны, если использовать deconnet, то из производной от ReLu берётся ещё один ReLu — это позволяет не передавать обратно отрицательные значения, но как вы видели, результат «так себе».
Но что, если совместить эти два метода?
class GuidedBackprop(ModifiedBackprop):
def grad(self, inputs, out_grads):
(inp,) = inputs
(grd,) = out_grads
dtype = inp.dtype
return (grd * (inp > 0).
astype(dtype) * (grd > 0).
astype(dtype),)
Тогда вы получите совершенно чистое и интерпретируемое изображение.
Карты значимости с использованием управляемого обратного распространения ошибки
Идите глубже
Теперь давайте подумаем, что это нам даёт? Напомню, что каждый сверточный слой представляет собой функцию, которая получает на вход трехмерный тензор, а на выходе также выдает трехмерный тензор, возможно, другой размерности.д Икс ш Икс час ; д epth — количество нейронов в слое, каждый из них генерирует карту признаков размером ш igth х час восемь.
Давайте попробуем следующий эксперимент в сети ВГГ-19 :
- для каждого слоя нейронной сети будем сортировать тайлы по значению суммы активаций внутри тайлов
— это даст нам наиболее выраженные особенности на изображении (один блок содержит активации одного и того же признака в разных пространственных координатах); - затем на каждом блоке мы выберем максимальный элемент – это даст нам позицию, где этот признак выражен наиболее ярко;
- а теперь возьмем производную по входному изображению с фиксированным значением одной позиции на одной пластине и остальными значениями в слое, установленными в ноль - это даст нам представление о признаке, а также рецептивная область этого нейрона (на какую область изображения смотрит этот нейрон).
конв3_3
конв4_3
conv5_3
бассейн5
Теперь представим, что вместо максимума по блоку мы возьмем производную от значения суммы всех элементов блока по входному изображению.
Тогда очевидно рецептивная область группы нейронов покроет все входное изображение.
Для ранних слоев мы увидим яркие карты, из чего сделаем вывод, что это детекторы цвета, затем градиенты, затем края и так далее в сторону более сложных узоров.
Чем глубже слой, тем тусклее изображение.
Это объясняется тем, что более глубокие слои имеют более сложный паттерн, который они обнаруживают, а сложный паттерн появляется реже, чем простой, поэтому карта активации тускнеет. Первый метод подходит для понимания слоев со сложными узорами, а второй — только для простых.
conv1_1
conv2_2
конв4_3
Вы можете скачать более полную базу активаций для нескольких образов.
Нейронный алгоритм художественного стиля (2 сентября 2015 г.
) Итак, прошло пару лет с момента первой успешной трепанации нейросети.
У нас (в смысле человечества) есть в руках мощный инструмент, позволяющий понять, чему обучается нейронная сеть, а также удалить то, чему мы не очень хотим, чтобы она обучалась.
Авторы данной статьи разрабатывают метод, позволяющий одному изображению генерировать аналогичную карту активации некоторому целевому изображению, а возможно, даже более чем одному — это основа стилизации.
Мы применяем к входным данным белый шум и, используя тот же итеративный процесс, что и в глубоком сне, уменьшаем это изображение до изображения, карты признаков которого аналогичны целевому изображению.
Потеря контента
Как уже говорилось, каждый слой нейронной сети выдает трехмерный тензор некоторой размерности.
Обозначим выход я -й слой из входа как
.
Тогда, если мы минимизируем взвешенную сумму остатков между входным изображением
и какой-то образ, к которому мы стремимся с , то вы получите именно то, что вам нужно.
Может быть.
Чтобы поэкспериментировать с этой статьей, вы можете использовать этот волшебный ноутбук , там происходят вычисления (как на GPU, так и на CPU).
Графический процессор используется для расчета характеристик нейронной сети и значения функции стоимости.
Theano создает функцию, которая может вычислять градиент целевой функции.
eval_grad по входному изображению Икс .
Затем эти данные передаются в lbfgs, и начинается итерационный процесс.
# Initialize with a noise image
generated_image.set_value(floatX(np.random.uniform(-128, 128, (1, 3, IMAGE_W, IMAGE_W))))
x0 = generated_image.get_value().
astype('float64') xs = [] xs.append(x0) # Optimize, saving the result periodically for i in range(8): print(i) scipy.optimize.fmin_l_bfgs_b(eval_loss, x0.flatten(), fprime=eval_grad, maxfun=40) x0 = generated_image.get_value().
astype('float64')
xs.append(x0)
Если запустить оптимизацию такой функции, то быстро получим изображение, похожее на целевое.
Теперь мы можем использовать белый шум для воссоздания изображений, похожих на какое-либо изображение контента.
Потеря контента: conv4_2 Изображение контента
Процесс оптимизации
Нетрудно заметить две особенности полученного изображения:
- теряются цвета — это результат того, что в конкретном примере использовался только слой conv4_2 (или, другими словами, вес w для него был ненулевым, а для остальных слоев — нулевым); как вы помните, именно ранние слои содержат информацию о цветах и градиентных переходах, а поздние содержат информацию о более крупных деталях, что мы и наблюдаем – цвета теряются, а содержание – нет;
- некоторые дома «сдвинулись», т.е.
прямые линии слегка искривились — это связано с тем, что чем глубже слой, тем меньше информации о пространственном положении объекта он содержит (результат использования свёрток и пулинга).
Потеря контента: Con1_1, Con2_1, Con4_2.
Надеюсь, теперь вы чувствуете, что у вас есть некоторый контроль над тем, что перерисовывается на изображении с белым шумом.
Потеря стиля
И вот мы переходим к самому интересному: как передать стиль? Что такое стиль? Очевидно, что стиль — это не то, что мы оптимизировали в Content Loss, поскольку он содержит много информации о пространственном положении объектов.Поэтому первое, что нужно сделать, — это как-то убрать эту информацию из представлений, полученных на каждом уровне.
Автор предлагает следующий метод. Возьмем тензор на выходе определенного слоя, разложим его по пространственным координатам и посчитаем ковариационную матрицу между штампами.
Обозначим это преобразование как г .
Что мы на самом деле сделали? Можно сказать, что мы рассчитали, как часто признаки внутри патча встречаются парами, или, другими словами, мы аппроксимировали распределение признаков в патчах многомерным нормальным распределением.
Затем потеря стиля вводится следующим образом, где с - это какое-то изображение со стилем:
Попробуем это ради Винсента? Получаем, в принципе, ожидаемое — шум в стиле Ван Гога, информация о пространственном расположении черт совершенно теряется.
Винсент
Что, если вместо изображения стиля поставить фотографию? Вы получите знакомые черты, знакомые цвета, но пространственное положение совершенно потеряется.
Фотография с потерей стиля
Вы, наверное, задавались вопросом, почему мы вычисляем ковариационную матрицу, а не что-то другое? В конце концов, существует множество способов агрегировать объекты так, что пространственные координаты теряются.
Это действительно открытый вопрос, и если взять что-то очень простое, то результат кардинально не изменится.
Давайте это проверим, мы будем рассчитывать не ковариационную матрицу, а просто среднее значение каждой пластины.
простая потеря стиля
Комбинированная потеря
Естественно, возникает желание смешать эти две функции затрат. Затем мы сгенерируем изображение из белого шума так, чтобы оно сохранило особенности изображения контента (которые связаны с пространственными координатами), а также содержало «стилевые» особенности, которые не связаны с пространственными координатами, т. е.мы будем надеяться, что Детали изображения контента останутся нетронутыми на своих местах, но будут перерисованы в нужном стиле.
На самом деле регуляризатор тоже есть, но мы для простоты его опустим.
Осталось ответить на следующий вопрос: какие слои (веса) следует использовать при оптимизации? И боюсь, что ответа на этот вопрос у меня нет, как и у авторов статьи.
У них есть предложение использовать следующее, но это совсем не значит, что другая комбинация будет работать хуже, слишком велико пространство поиска.
Единственное правило, которое следует из понимания модели: нет смысла брать соседние слои, поскольку их характеристики не будут сильно отличаться друг от друга, поэтому в стиль добавляется слой из каждой группы conv*_1. # Define loss function
losses = []
# content loss
losses.append(0.001 * content_loss(photo_features, gen_features, 'conv4_2'))
# style loss
losses.append(0.2e6 * style_loss(art_features, gen_features, 'conv1_1'))
losses.append(0.2e6 * style_loss(art_features, gen_features, 'conv2_1'))
losses.append(0.2e6 * style_loss(art_features, gen_features, 'conv3_1'))
losses.append(0.2e6 * style_loss(art_features, gen_features, 'conv4_1'))
losses.append(0.2e6 * style_loss(art_features, gen_features, 'conv5_1'))
# total variation penalty
losses.append(0.1e-7 * total_variation_loss(generated_image))
total_loss = sum(losses)
Окончательную модель можно представить следующим образом.
А вот и результат дома с Ван Гогом.
Пытаюсь контролировать процесс
Давайте вспомним предыдущие части, уже за два года до нынешней статьи другие учёные исследовали, чему на самом деле обучается нейронная сеть.Вооружившись всеми этими статьями, вы сможете создавать визуализации объектов в разных стилях, с разными изображениями, с разными разрешениями и размерами, а также пытаться понять, какие слои с каким весом использовать.
Но даже повторное взвешивание слоев не дает полного контроля над происходящим.
Проблема здесь более концептуальная: мы оптимизируем не ту функцию ! Как так, спросите вы? Ответ прост: эта функция минимизирует расхождение.
ну, вы поняли.
Но на самом деле мы хотим, чтобы изображение нам нравилось.
Выпуклая комбинация функций потери контента и стиля не является мерой того, что наш разум считает красивым.
Замечено, что если продолжать стилизацию слишком долго, функция стоимости, естественно, падает все ниже и ниже, но эстетическая красота результата резко падает.
Ну да ладно, есть еще одна проблема.
Допустим, мы нашли слой, который извлекает нужные нам признаки.
Допустим, некоторые текстуры треугольные.
Но этот слой также содержит множество других объектов, таких как круги, которые мы действительно не хотим видеть на полученном изображении.
Вообще говоря, если бы мы могли нанять миллион китайцев, мы могли бы визуализировать все особенности имиджа стиля, а перебором просто отметить те, которые нам нужны, и включить их только в функцию стоимости.
Но по понятным причинам все не так просто.
Но что, если мы просто удалим все круги, которые не хотим видеть в результате изображения стиля? Тогда активация соответствующих нейронов, реагирующих на кружочки, просто не сработает. И, естественно, тогда на полученной картинке этого не будет. То же самое и с цветами.
Представьте себе яркое изображение с множеством цветов.
Распределение цветов будет очень размазано по всему пространству, и распределение результирующего изображения будет таким же, но в процессе оптимизации вероятно будут потеряны те пики, которые были на оригинале.
Оказалось, что простое уменьшение бита Теги: #Машинное обучение #python #математика #Алгоритмы #Обработка изображений #компьютерное зрение #сверточная нейронная сеть #нейронные сети #математика #художник #деконволюционная нейронная сеть #теано #лазанья #без магии #перенос стиля
-
Шаги По Конвертации Avi В Ipad
19 Oct, 24 -
Север, Дуглас
19 Oct, 24 -
Remarkable — Планшет, Имитирующий Бумагу
19 Oct, 24 -
Эмодзи – Слово 2015 Года
19 Oct, 24 -
Обзор Программы Dotnext 2018 Питер
19 Oct, 24 -
Немного Социологии На Основе Событий
19 Oct, 24 -
Дискуссии По Геймдизайну Vr-Игр
19 Oct, 24 -
Плагин Для Smarty - Комбайн
19 Oct, 24