Недавно я экспериментировал с вариационными автоэнкодерами, впервые представленными Kingma et al., 2013. В частности, я попытался обучить вариационный автоэнкодер на наборе данных MNIST. Вы можете посмотреть код на мой профиль GitHub.

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

Трюк с репараметризацией

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

Вместо этого мы обучаем два скрытых вектора, один из которых представляет собой среднее значение, а другой — стандартное отклонение изученной скрытой переменной. После этого мы делаем выборку из этих дистрибутивов следующим образом:

Таким образом, S_i — случайный вектор, выбранный из нормального распределения, Z_sampled — выбранный скрытый вектор, mu_i — вектор средних, sigma_i — вектор стандартных отклонений, а точка в кружке указывает на произведение Адамара (поэлементное умножение).

Расчет расхождения KL для 2 гауссианов

Учитывая случай моделирования латентного вектора как переменной Гаусса, мы можем фактически явно вычислить член дивергенции KL. Хотя это описано в приложении к статье, вы можете пропустить расчет. К счастью, это не слишком сложно. Это просто требует, чтобы вы усердно следовали формуле KL-дивергенции непрерывных распределений:

После некоторых вычислений и упрощений (на самом деле вам не придется вычислять какие-либо интегралы, так как вы сможете применить некоторые приемы) вы придете к правильной формуле:

И снова i перебирает весь скрытый вектор.

Для получения подробной информации о расчете я нашел эту ссылку очень полезной: https://stats.stackexchange.com/questions/7440/kl-divergence-between-two-univariate-gaussians.

Схема прогрева для дивергенции KL

Обучение генеративных моделей по своей природе сложно. Это связано с тем, что всегда требуется уравновешивание (по крайней мере) двух членов в функции потерь. Эти два термина обязательно противопоставляются друг другу. Например, в GAN дискриминационная сеть противостоит генеративной. Точно так же в VAE член потерь, соответствующий потерям L2 на сходстве входа и выхода, противодействует потерям KL, которые пытаются кодировать вход как нормально распределенную скрытую переменную.

Я обнаружил, что введение двух терминов потерь на разных этапах тренировочного процесса может помочь в обучении. Думаю, что это связано с тем, что потери КЛ изначально очень большие. Следовательно, алгоритму градиентного спуска может показаться, что лучший способ минимизировать потери — минимизировать потери KL только. В некоторых случаях это может привести к локальному минимуму, из которого оптимизация не может выбраться. Я обнаружил, что введение схемы прогрева для термина дивергенции KL является хорошим решением. Я постепенно увеличиваю вес терма от 0 до 1 в течение 10 эпох. Это позволяет сети сначала адаптироваться к потерям L2 и постепенно изменять способ распределения скрытого вектора после того, как она нашла хорошее приближение к входным данным.