Недавно закончил Урок 8 Практического глубокого обучения Джереми Ховарда (часть 2 в бета-версии). Здесь я собираюсь проделать небольшую работу и результаты воссоздания записной книжки Jupyter из урока. ‹это очень грубо

[ПРИМЕЧАНИЕ: думаю, что часть 2 была переименована в «Cutting Edge Deep Learning» - скоро должна быть выпущена публично. Также tl; dr: прокрутите вниз для видео]

Мое представление записной книжки можно найти здесь: https://github.com/WNoxchi/Kaukasos/blob/master/FAI02/L8_NeuralArtTrsfr_CodeAlong.ipynb

После просмотра импорта библиотеки и необходимости указать limit_mem(), чтобы TensorFlow не поглощал все, что может предложить ваш графический процессор, мне пришлось узнать, что такое травление в Python. Оказывается, благодаря sentdex на YouTube, это всего лишь сериализация на Python. Какая сериализация? Очевидно, он отправляет один поток за другим ... очевидно, он полезен, когда вы выполняете много операций с диском (например, считываете данные для машинного обучения), хотя он не обеспечивает безопасности, поэтому будьте осторожны с сетевыми потоками.

Я поэкспериментировал, написав мини-словарь латыни: Буквы мкседрули:

Вот и все. Я не смотрел новый видеоролик от sentdex Pickling and Scaling - Practical Machine Learning with Python p.6 », но могу, если чувствую, что должен.

1. РЕКОНСТРУКЦИЯ СОДЕРЖИМОГО

Передача нейронного стиля

NST создает новое изображение, которое сохраняет «стиль» одного изображения и форму другого. Так, например, можно создать изображение кошки, написанное в стиле Винсента Ван Гога, с использованием двух входных изображений: кошки и другого, представляющего стиль рисования Ван Гога.

Здесь важно несколько вещей. Алгоритм не применяет стиль Ван Гога к самому изображению кошки: он начинает со свежего изображения (например, случайного набора пикселей) и выстраивает его в желаемую композицию.

Потребуется модель, которая уже хорошо / достаточно хорошо распознает особенности обоих входных изображений. Итак, «где же глаза?», «Это лицо», «этот мазок - вещь» и т. Д.

Первым шагом является инициализация модели (здесь модифицированная CNN VGG16 с использованием среднего пула и минус верхний блок уровней FC):

Затем мы определяем граф вычислений в Keras. Граф вычислений, насколько я понимаю до сих пор, определяет серию операций над заменами данных. Данные еще не переданы, но когда они будут переданы, программа будет знать, что делать. Мы делаем это, чтобы определить целевое изображение

Как видите, targ - это результат (прогноз) layer_model на img_arr. layer_model - это Model (keras.models.Model: Keras Functional API), состоящий из слоев model от входа до layer, который мы определяем как 1-й сверточный уровень 5-го Conv-блока модели VGG16. img_arr - исходное изображение, которое было предварительно обработано так же, как авторы VGG предварительно обработали свои данные ImageNet при обучении своей модели.

ПРИМЕЧАНИЕ: layer - это активация вывода с конца нашей сверточной подмодели. layer_model - это подмодель, определенная от начала исходной модели до layer. targ - это цель-активации. Функция потерь пытается свести layer и targ как можно ближе друг к другу.

Эта формулировка может сбивать с толку, но она становится более понятной, когда вы просматриваете ее в коде. В этом блокноте мы определяем несколько таких графиков с помощью Keras.

Чтобы алгоритм мог оценить, насколько хорошо он работает, необходимо как-то квантовать его прогресс: функция потерь / цели.

Для этого мы определяем потерю как среднеквадратичную ошибку между выходным слоем и целевым изображением.

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

Это evaluator вместе с числом итераций и x случайным изображением входит в solve_image(•), который выполняет работу по оптимизации потерь.

Все это делается для воссоздания изображения из оригинала, применяя его свойства conv-функции к случайно сгенерированному изображению и обновляя функцию потерь.

Основная идея такова: мы пропускаем исходное и новое случайное изображение через подмодель и используем нашу функцию потерь, чтобы определить, насколько близко они совпадают. Мы используем алгоритм Line-Search из scipy.optimize.fmin_l_bgfs_b в качестве оптимизатора, чтобы минимизировать потери. Когда мы в следующий раз будем работать над включением стиля из стиля-изображения, мы будем использовать веса, чтобы сбалансировать то, насколько мы хотим сосредоточиться на воссоздании исходного изображения, а не на применении стиля стиля изображения.

Итак, что мы в итоге получаем, используя моего кота Терека в качестве исходного изображения:

И создание изображения из случайных пикселей:

И проделываем это через solve_image(•) на 10 итераций:

Получаем это безбожное чудовище:

Как бы плохо это ни выглядело, на самом деле это действительно интересно. Помните, что этот алгоритм берет мозаику или случайные пиксели и воссоздает изображение кошки, пропуская их через CNN, который мы определили выше, и оптимизирует веса и активации этой сети на основе среднеквадратичной ошибки между новым (целевым) изображением. и активация оригинала в сети. Это означает, что целевое изображение будет построено в соответствии с характеристиками, выраженными в сверточной сети. Таким образом, модель должна действительно отличать глаза от фона и так далее. Нижние сверточные блоки дадут нам более тонкие функции, а последующие дадут нам больше макро-функций в модели. Таким образом, мы можем получить результат, который больше похож на исходное изображение, используя более ранние сверточные слои.

2. РЕКОНСТРУКЦИЯ СТИЛЯ

Чтобы воссоздать стиль, нам нужно сделать кое-что еще. Мы меняем функцию потерь, рассчитывая ее для нескольких слоев. Цитата из оригинальной записной книжки:

Если раньше мы вычисляли MSE необработанных сверточных выходных данных, здесь мы преобразуем их в «матрицу Грамиана» их каналов (то есть произведение матрицы на ее транспонирование) перед тем, как взять их MSE. Непонятно, почему это помогает нам достичь нашей цели, но это работает. Одна мысль состоит в том, что грамиан показывает, как наши особенности на этом сверточном слое коррелируют, и полностью удаляет всю информацию о местоположении. Таким образом, сопоставление с матрицей Грама каналов может соответствовать только некоторому типу информации о текстуре, но не информации о местоположении.

Таким образом, мы можем создать стиль этой картины Ван Гога:

И примените этот стиль к этому случайно сгенерированному целевому изображению:

Чтобы получить изображение из случайных пикселей в стиле Ван Гога после 10 итераций:

3. ПЕРЕДАЧА СТИЛЯ

И, наконец, о передаче стиля. У нас получилось 1. Ввод отдыха. 2. Стиль отдыха. Теперь, чтобы собрать это воедино:

Это делается (в конечном итоге) интуитивно понятным способом: объедините два подхода путем взвешивания и добавления двух функций потерь. Как и раньше, мы берем последовательность выходных данных слоев для вычисления потери стиля. Для вычисления потери контента требуется только один выходной слой. Чем ниже уровень, тем точнее реконструкция содержимого. Когда мы объединяем реконструкцию контента со стилем: более ранняя / более свободная реконструкция даст больше места для стиля, и наоборот.

Здесь у нас есть код, который делает style_layers 2-м сверточным слоем блоков с 1 по 5; и content_layer выходы (активации) блока 4 свёрточного слоя 2:

Это создает три отдельных типа вывода: для исходного изображения, style-image и случайного изображения, пиксели которого мы обучаем.

Наконец, создается массив весов для применения к активациям вывода стиля. Это настраивает микс на этих уровнях реконструкции изображения и передачи стиля. ПРИМЕЧАНИЕ: это работает как фактор потери контента. Чем меньше коэффициент (больший знаменатель), тем больший эффект имеет стиль для окончательного изображения, пока он полностью не закроет все исходное содержимое.

Надо найти золотую середину между универсальным и деловым.

И вот некоторые результаты:

Мне случайно попалась импрессионистическая картина вилки питания:

Сверточным нейронным сетям требуется фиксированный размер входных данных, поэтому содержимое, стиль и выходные изображения должны иметь одинаковые размеры. Поэтому, получая совместимую обрезку фотографии моей кошки, я забыл отрегулировать ее, получив в верхнем левом углу те же размеры, что и крошечный рисунок птицы, который я использовал для стиля.

И, наконец, просто убедился, что все делаю правильно: проверяю, могу ли я получить те же результаты на тех же входных изображениях, что и Джереми Ховард:

Определенно некоторые различия, наверняка сходство, мне это нравится для первого запуска. Я определенно рад сделать хороший перенос стилей, как то, что вы найдете здесь: https://github.com/titu1994/Neural-Style-Transfer

К счастью, похоже, что такие техники, как «маскировка» и другие, будут рассмотрены в Уроке 9, так что мне, возможно, даже не придется учить это на стороне.

Итак, последнее замечание: это был пост не для обучения с нуля. Для этого: вам нужно пройти урок и оригинальную тетрадь. Но если вы работаете над этим и хотите еще раз подумать, надеюсь, это было полезно.

Еще одна вещь, которая мне нравится в AI, - это все возможности для схематичных, но точных названий папок:

Большое спасибо Джереми Ховарду и Рэйчел Томас за создание fast.ai.