06 / 33 · Основы
Автоэнкодеры
Реконструкция как путь к представлению — свёрточный автоэнкодер на MNIST, функция потерь реконструкции, шумоподавляющий и разреженный варианты и то, почему обычный автоэнкодер ещё не генеративная модель.
На этой странице 9
Из блоков предыдущей заметки — свёрток, батч-нормализации (batch normalization) и LeakyReLU — мы можем построить первую нейросетевую модель курса. Пока она ничего не генерирует. Автоэнкодер обучается воспроизводить собственный вход; задача обучить сеть обратным распространением ошибки пропускать вход через узкий скрытый слой и обратно встречается уже у Rumelhart, Hinton and Williams (1986) как «задача кодирования» (encoding problem). Реконструкция — лишь предлог. На самом деле нам нужно представление (representation): компактный код, который сеть вынуждена построить по пути (Hinton and Salakhutdinov, 2006; Goodfellow, Bengio and Courville, 2016, глава 14). В этой заметке мы обучаем на MNIST обычный, шумоподавляющий и два разреженных (KL и L1) свёрточных автоэнкодера, смотрим, что улавливают их коды и как это измерить, и в конце обсуждаем пробел, который закроет вариационный автоэнкодер.
Обозначения. — входное изображение, — распределение данных, из которого оно взято. Энкодер с параметрами переводит в латентный код . Декодер с параметрами переводит код обратно в реконструкцию . Как и в заметке о VAE, относится к энкодеру, а — к декодеру. и — веса штрафов за разреженность, которые появятся ниже; они не связаны с расписанием шума (noise schedule) из заметок о диффузии.
Архитектура: понижение разрешения, узкое место, повышение разрешения#
Энкодер переводит вход в латентный код , а декодер переводит код обратно в пространство входов:
Путь данных устроен просто — от входа через узкое место (bottleneck) к реконструкции:
Узкое место меньше входа, поэтому сеть не может пропустить пиксели через себя без изменений. Ей приходится сохранить то, что нужно для восстановления входа, и отбросить остальное. В этом и состоит идея латентного пространства: каждый вход становится точкой в пространстве меньшей размерности, которое выучивается по данным.
Разрешение входа: . Цифры MNIST масштабируются до (transforms.Resize((64, 64)), затем ToTensor(), который уже даёт значения пикселей в ). Большее изображение позволяет модели понижать разрешение постепенно. Энкодер четыре раза уменьшает пространственный размер вдвое: свёртка с шагом (stride) 2, ядром 3 и дополнением (padding) 1 переводит в (формула размера выхода). Декодер четыре раза удваивает размер:
| Сторона | Энкодер, 4 уменьшения вдвое | Декодер, 4 удвоения | Размер входа восстановлен? |
|---|---|---|---|
| да | |||
| нет () |
Если сторона делится на , как , каждый этап уменьшает её ровно вдвое, и зеркальный декодер возвращается к размеру входа. При те же четыре этапа оставили бы карту и выход .
Свёрточный автоэнкодер слой за слоем
Модель. Каждый этап энкодера — блок из свёртки с шагом 2, батч-нормализации и LeakyReLU с наклоном (все они объяснены в предыдущей заметке). Последняя свёртка с шагом 1 выдаёт код. Каждый этап декодера повышает разрешение в 2 раза билинейной интерполяцией, а затем применяет свёртку , батч-нормализацию и LeakyReLU. Финальная свёртка переводит 16 каналов в 1, за ней следует сигмоида.
| Тензор | Форма (каналы × высота × ширина) | Чисел |
|---|---|---|
| вход | ||
| после блока энкодера 1 | ||
| после блока энкодера 2 | ||
| после блока энкодера 3 | ||
| после блока энкодера 4 и последней свёртки: код | ||
| после блока декодера 1 | ||
| после блока декодера 2 | ||
| после блока декодера 3 | ||
| после блока декодера 4 | ||
| выход |
Размер латентного кода. Узкое место имеет форму , то есть содержит чисел: развёрнутый в вектор латентный код 512-мерный. Это в раз меньше чисел, чем во входе после масштабирования, но лишь примерно в раза меньше, чем исходных пикселей . Сжатие умеренное: важна структура кода, а не его размер.
Как читать энкодер и декодер#
- Энкодер работает как обучаемый фильтр, а не как фиксированное сжатие. Каждый шаг понижения разрешения — обучаемая свёртка с шагом 2. Фильтры извлекают признаки, а шаг снижает разрешение, так что сеть сама решает, что сохранится при каждом уменьшении вдвое; фиксированное масштабирование на это не способно. Классическая альтернатива — свёртка с шагом 1, за которой следует пулинг (pooling), уменьшающий размер вдвое по фиксированному правилу: max-пулинг (max pooling) оставляет наибольшее значение каждого окна , усредняющий пулинг (average pooling) — их среднее. В классификаторах изображений замена max-пулинга свёрткой с шагом 2 не снизила долю правильных ответов (accuracy) в экспериментах Springenberg et al. (2015), а понижение разрешения при этом тоже становится обучаемым.
- Декодер не сводится к увеличению изображения. Он работает по схеме повышение разрешения + свёртка. Билинейное повышение разрешения — фиксированная операция без параметров; детали восполняет обучаемая свёртка после него.
- Сжатие возникает из архитектуры, а не из явного требования. Ни одному слою не предписано сжимать. Первый блок энкодера даже расширяет входных значений до активаций. Код становится маленьким только потому, что формы тензоров сужаются к узкому месту.
- Слои выучивают признаки, а не пиксели. Ранние слои реагируют на локальные паттерны, например границы и фрагменты штрихов. Более глубокие слои через своё рецептивное поле видят бо́льшую часть изображения и комбинируют эти паттерны в более крупные части цифры, поэтому каналы кодируют признаки, а не значения отдельных пикселей.
Функция потерь реконструкции#
Функция потерь сравнивает выход со входом. Стандартный выбор — среднеквадратичная ошибка (mean squared error, MSE) по всем скалярным компонентам,
усреднённая по данным:
Для автоэнкодера и . nn.MSELoss() в PyTorch усредняет по всем пикселям батча: для батча из 64 изображений.
MSE или BCE. Значения пикселей лежат в , поэтому естественная альтернатива — бинарная кросс-энтропия (binary cross-entropy, BCE):
усреднённая по пикселям, как это по умолчанию делает nn.BCELoss. Она трактует каждый пиксель как вероятность того, что он «включён», и это подходит для почти бинарных цифр MNIST.
Двум функциям потерь соответствуют два правдоподобия декодера. Для гауссовского декодера с фиксированной дисперсией на пикселях , поэтому MSE совпадает с его отрицательным логарифмом правдоподобия с точностью до положительного множителя и аддитивной константы. BCE — отрицательный логарифм правдоподобия декодера, в котором каждый пиксель — независимая бернуллиевская величина со средним . Строго говоря, это верно только для бинарных пикселей. В масштабированных изображениях MNIST много серых значений; для них BCE по-прежнему минимальна при , но уже не является отрицательным логарифмом нормированной плотности на (Loaiza-Ganem and Cunningham, 2019). Заметка о VAE опирается на это прочтение функции потерь как правдоподобия.
Поскольку сигмоида уже применяется в forward, для BCE нужна nn.BCELoss; nn.BCEWithLogitsLoss ожидает «сырые» логиты и сама применяет сигмоиду, поэтому в сочетании с сигмоидой на выходе сигмоида применилась бы дважды. Численно более устойчивый вариант — убрать сигмоиду и применять nn.BCEWithLogitsLoss к логитам.
Диапазон входа должен подходить и к выходной активации, и к функции потерь. MNIST после ToTensor() уже лежит в , поэтому дополнительная нормализация здесь не нужна; для других данных она обычно нужна. Для цветных изображений, например изображений CIFAR-10 размера , часто переводят в отображением , которое переводит входы в диапазон, симметричный относительно нуля, а для показа переводят выходы обратно отображением . Тогда декодер должен уметь выдавать отрицательные значения (см. выходную активацию ниже), и BCE больше не подходит, поскольку её целевые значения должны лежать в ; MSE подходит.
Возникающая структура#
Ничто в функции потерь не требует упорядоченного латентного пространства, а метки при обучении не используются. Тем не менее структура возникает. Если спроецировать 512-мерные коды тестовой выборки на плоскость с помощью t-SNE (van der Maaten and Hinton, 2008) и раскрасить точки по цифрам, видны кластеры по цифрам, разделённые чище, чем до обучения. Большинство цифр образует по одному компактному кластеру; двойки распадаются на две группы, а единицы образуют вытянутую полосу. Коды упорядочены по содержанию, а не по значениям отдельных пикселей.
t-SNE изображает окрестности, а не расстояния. Метод располагает точки так, чтобы ближайшие соседи каждого кода в 512-мерном пространстве оставались рядом с ним и на плоскости; перплексия, здесь 30, примерно задаёт, сколько соседей учитывается. Размеры кластеров на графике и промежутки между ними несут мало информации (Wattenberg, Viégas and Johnson, 2016). График призван показать, являются ли соседи кода кодами той же цифры, и даже это лишь качественное свидетельство: вложение может разрывать или склеивать окрестности (t-SNE и UMAP). Проверять это нужно в исходном 512-мерном пространстве, например по доле ближайших соседей кода с той же меткой или зондирующим классификатором ниже.
Измерение кода классификатором. График даёт лишь качественную картину. Число даёт зондирующий классификатор (probe): замораживаем энкодер, вычисляем коды размеченного набора, обучаем на них простой классификатор и сообщаем его долю правильных ответов на отложенных (held-out) изображениях (Alain and Bengio, 2016; они используют линейные классификаторы на слоях сети). Классификатор видит только код, поэтому его доля правильных ответов измеряет, сколько информации о классе код сохраняет в легко извлекаемом виде. Случайные свёрточные признаки уже информативны, поэтому точка отсчёта — не случайное угадывание, а тот же зондирующий классификатор на кодах необученного энкодера той же архитектуры. Обучение улучшило представление, только если обученный энкодер заметно превосходит эту точку отсчёта. Те же коды могут также заменить признаки Inception в FID, когда изображения не похожи на ImageNet.
Два эксперимента исследуют латентное пространство напрямую:
- Интерполяция. Кодируем «7» и «2» и декодируем для 8 кадров, . Крайние кадры — чёткие цифры, но средние выглядят как наложение двух начертаний, а не как цифры, плавно перетекающие одна в другую.
- Сэмплирование. Для каждого класса цифр подгоняем к кодам обучающих изображений гауссиану с осями вдоль координат (диагональную): берём среднее и стандартное отклонение по каждой координате, так что 512 координат сэмплируются независимо. Берём по одному коду на класс и декодируем его. В сохранённом запуске шесть из десяти сэмплов — узнаваемые цифры, а 2, 4, 5 и 8 сильно искажены. Для каждого класса взят лишь один сэмпл.
Оба эксперимента показывают, почему обычный автоэнкодер выучивает представления, но ещё не является генеративной моделью. Ничто в обучении не заставляет произвольную точку латентного пространства декодироваться в правдоподобное изображение, а коды не следуют никакому распределению, из которого мы умеем сэмплировать: диагональная гауссиана игнорирует корреляции между координатами, сэмплирование по классам потребовало меток и статистик по каждому классу, а области между кластерами вообще не обучались во что-либо декодироваться. Вариационные автоэнкодеры закрывают именно этот пробел: они делают распределение кодов частью целевой функции обучения.
Варианты#
Шумоподавляющий автоэнкодер#
Шумоподавляющий автоэнкодер (denoising autoencoder; Vincent et al., 2008) меняет задачу, а не архитектуру:
- Испортить вход случайным искажением (corruption) . В исходной статье случайная доля компонент входа обнуляется; другой распространённый выбор — аддитивный гауссовский шум (Vincent et al., 2010).
- Восстановить чистый вход по испорченному, , и вычислить функцию потерь относительно , а не .
- В результате получается устойчивое представление: его признаки остаются стабильными, когда часть входа испорчена.
Сеть не может добиться успеха, выучив тождественное отображение, поэтому ей приходится выучить, какая структура в данных — сигнал, а какая — шум. Позже в курсе эта идея станет центральной. При гауссовском искажении обучение денойзера (модели шумоподавления) эквивалентно score matching — сопоставлению градиентов логарифма плотности — для сглаженной версии плотности данных (Vincent, 2011), а целевая функция предсказания шума диффузионной модели обучает денойзер на многих уровнях шума.
Шумоподавляющий автоэнкодер: искажаем вход, сравниваем с чистым изображением
Здесь искажение — аддитивный гауссовский шум,
без обрезки значений, так что искажённые пиксели могут выходить за пределы . Кроме того, каждый блок энкодера и декодера применяет Dropout2d(0.3), который во время обучения случайно отключает целые карты признаков: это второй, внутренний вид искажения. Функция потерь — та же MSE относительно чистого изображения. После 5 эпох функция потерь шумоподавляющей модели на обучении равна против у обычного автоэнкодера. Эти числа нельзя сравнивать напрямую: шумоподавляющая модель решает более трудную задачу — с зашумлённым входом и включённым дропаутом (dropout).
На тестовых цифрах, искажённых тем же способом, обученная модель полностью убирает шум: фон получается равномерно чёрным. Штрихи выходят немного толще и глаже, чем в оригинале, а мелкие детали, например короткий крючок вверху нуля, теряются. Коды ведут себя так же, как у обычной модели. До обучения большинство цифр уже образует кластеры, а 4 и 9 перекрываются; после обучения кластеров десять, и снова двойки распадаются на две группы, а единицы образуют вытянутую полосу. Сэмплирование по классам, как выше, даёт в основном узнаваемые, но размытые цифры (по одному сэмплу на класс); 4 и 5 сильно искажены.
Разреженный автоэнкодер#
Разреженный автоэнкодер (sparse autoencoder) сохраняет функцию потерь реконструкции и добавляет штраф за разреженность (sparsity penalty), который поощряет редкие активации: каждый скрытый нейрон должен быть активен лишь на небольшой доле входов (Ng, 2011). Вместе с функцией потерь реконструкции это должно приводить к тому, что каждый вход задействует лишь несколько сильно активных нейронов. Тогда скрытый слой может быть большим, даже больше входа, и сеть при этом не сможет просто скопировать вход.
Распространены два варианта.
KL-штраф на промежуточных слоях. Скрытые нейроны используют сигмоидные активации после батч-нормализации, поэтому каждая активация лежит в и её можно читать как частоту срабатывания (firing rate). Для каждого нейрона обозначим через его среднюю активацию по батчу; Ng (2011) усредняет по всей обучающей выборке, и среднее по батчу служит оценкой этого среднего. Штраф сравнивает с целевой частотой через KL-дивергенцию (KL divergence) между двумя распределениями Бернулли:
здесь и .
Плотный и разреженный коды: к чему стремится штраф за разреженность
Это KL-дивергенция между монетой, которая «срабатывает с вероятностью », и монетой, которая «срабатывает с вероятностью ». Она равна нулю, только когда средняя частота срабатывания равна . Штраф ограничивает лишь среднюю частоту каждого нейрона: нейрон, который на каждом входе слабо срабатывает на уровне , тоже ему удовлетворяет. Разреженность для каждого отдельного входа должна возникнуть из взаимодействия с функцией потерь реконструкции. Сумма берётся по всем каналам и позициям четырёх промежуточных слоёв энкодера: слагаемых. Декодер этой модели тоже отличается от остальных: вместо повышения разрешения со свёрткой в нём транспонированные свёртки (transposed convolution; ядро 4, шаг 2, дополнение 1). При расширении (dilation) 1 и без выходного дополнения (output padding) формула размера выхода ConvTranspose2d даёт удвоенную сторону:
L1-штраф на код. Это обычная архитектура плюс штраф с весом на среднее абсолютное значение 512 элементов кода, которое тоже усредняется по батчу:
(Суб)градиент равен и имеет постоянную величину, поэтому L1-штраф продолжает тянуть малые элементы к нулю, тогда как притяжение квадратичного штрафа вблизи нуля исчезает. Вследствие этого в точке минимума целевой функции с L1-штрафом часть элементов обычно в точности равна нулю: минимизация применяет к ним мягкое пороговое отсечение (soft thresholding) (Goodfellow, Bengio and Courville, 2016, раздел 7.1.2). Обычное градиентное обучение с Adam, как здесь, лишь приближает такие элементы к нулю, и они продолжают колебаться около него.
Ни один из вариантов здесь не работает хорошо, и причины поучительны. Если средние по батчу у скрытых сигмоид в начале близки к — а батч-нормализация при инициализации центрирует их входы, — KL-слагаемое начинается примерно с . Это оценка по порядку величины, поскольку средние отдельных нейронов различаются; так как выпукла, разброс вокруг среднего значения может её только увеличить (неравенство Йенсена).
Записывалась только сумма двух слагаемых. Она падает с в первую эпоху до в пятую и всё ещё быстро снижается ( в четвёртую эпоху), так что обучение не сошлось. Полностью чёрный выход дал бы MSE, равную среднему квадрату значения пикселя, — около для масштабированных изображений MNIST (это значение вычислено по тестовой выборке MNIST с тем же билинейным масштабированием). Сохранённые реконструкции KL-модели хуже, чем у обычной модели, но далеко не пустые, и это наводит на мысль, что значительная часть приходится на штраф. Разделения это не ограничивает: чёрный выход — лишь одно допустимое базовое решение, а не верхняя граница ошибки реконструкции несошедшейся модели, которая может быть и больше. Вопрос решила бы раздельная запись слагаемого реконструкции и штрафа. Масштаб штрафа указывает в ту же сторону: при 27136 слагаемых вес слишком велик. Реконструкции KL-модели заметно хуже, а её график t-SNE раздроблен. Сэмплирование по классам из её кодов полностью проваливается: ни один из десяти декодированных сэмплов не является узнаваемой цифрой, это лишь обрывки штрихов.
У L1-варианта противоположная проблема. При штраф заметно не меняет обучение: функция потерь в конце равна , практически как у обычной модели, и при одном запуске каждой модели эта разница в пределах шума. Всё остальное похоже на обычную модель: десять кластеров на графике t-SNE и реконструкции так же точны, как у обычной модели; сэмплы по классам несколько хуже: 0, 1, 6 и 7 чёткие, остальные цифры искажены. Кроме того, штраф можно обойти. Первый блок декодера применяет билинейное повышение разрешения и свёртку без смещения (обе операции линейны), а затем батч-нормализацию. Замена на при любом умножает выходы свёртки на , и батч-нормализация снова делит на этот множитель, пока остаётся намного больше её (батч-нормализация не замечает масштаба своего входа). Поэтому энкодер мог бы снижать штраф, уменьшая общий масштаб и не делая код разреженным. Масштаб не измерялся, так что это возможный обход, а не наблюдаемый; его показало бы сравнение среднего до и после обучения. Вес штрафов за разреженность нужно подбирать с учётом функции потерь реконструкции, а архитектура не должна позволять их обходить.
Транспонированная свёртка или повышение разрешения + свёртка?#
Оба способа удваивают пространственный размер. Транспонированная свёртка выучивает собственный фильтр повышения разрешения, но когда размер ядра не делится на шаг, отпечатки ядра перекрываются неравномерно и дают шахматные артефакты (checkerboard artifacts). Масштабирование с последующей свёрткой избегает неравномерного перекрытия и обычно считается более надёжным выбором по умолчанию (Odena, Dumoulin and Olah, 2016). То же исследование добавляет две оговорки. Лучшие результаты в нём получены с масштабированием методом ближайшего соседа, а билинейное масштабирование, которое использует здесь обычная модель, авторам было трудно заставить работать. Ядро, размер которого делится на шаг, как ядро 4 с шагом 2 в KL-модели, устраняет неравномерное перекрытие, но обученные ядра всё равно могут давать артефакты. Два декодера здесь не сравниваются в равных условиях, так что выбор между ними — архитектурное решение, а не измеренный результат.
Зачем сигмоида на выходе?#
Свёрточные блоки используют LeakyReLU с наклоном , (Maas, Hannun and Ng, 2013). Почему такие ненасыщающиеся активации заменяют сигмоиды внутри глубоких сетей, объяснено в предыдущей заметке. У сигмоиды здесь другая задача. Она сжимает любое действительное число в — диапазон интенсивностей пикселей, — то есть переводит неограниченный выход последней свёртки декодера в шкалу целевых значений. При она проходит через :
LeakyReLU и сигмоида
Сигмоида служит выходной активацией каждой модели в этой заметке. В модели с KL-разреженностью она также служит скрытой активацией, потому что бернуллиевскому штрафу нужны активации, которые можно читать как частоты срабатывания в .
Для целевых значений, масштабированных в , у сигмоиды неподходящий диапазон: отрицательного значения она не выдаст никогда. Тогда на выходе обычно ставят — сигмоиду, растянутую на :
Например, генератор DCGAN заканчивается , а его обучающие изображения масштабированы в (Radford, Metz and Chintala, 2016, разделы 3–4). С MSE работает и линейный выход вообще без активации; его значения не ограничены, и для показа их обрезают до допустимого диапазона.
Ноутбук#
Эксперименты следуют ноутбуку об автоэнкодерах из репозитория HSE-LAMBDA Deep Generative Models (закреплённый коммит; ячейки нумеруются с 1, считая все ячейки подряд, в том числе текстовые). Все значения функции потерь и наблюдения о графиках выше взяты из его сохранённых выходов; MSE полностью чёрного выхода вычислена по датасету.
- Ячейки 5–7: MNIST, масштабированный до и загруженный как тензоры со значениями в , батчами по 64 с
drop_last=True(ячейка 7). - Ячейка 9:
visualize_latent_space, которая разворачивает коды в векторы и проецирует их с помощью t-SNE (перплексия 30); она вызывается до и после обучения в ячейках 15 и 20. Она переключает модель вeval(), поэтому график до обучения строится с батч-нормализациями, которые ещё хранят начальные скользящие статистики (среднее 0, дисперсия 1) и потому фактически являются тождественными отображениями. - Ячейка 10 обозначает энкодер как , а декодер как — буквы противоположны принятым в этой заметке. Ячейка 11:
BlockиAutoEncoderс формами из таблиц выше. Ячейки 12–13 выбираютnn.MSELossи Adam с шагом обучения (learning rate) ; текстовая ячейка предлагает в качестве альтернативыnn.BCEWithLogitsLoss, что, как объяснено выше, привело бы к двойной сигмоиде. Ячейка 17 обучает модель 5 эпох. - Ячейки 22–25: реконструкции тестовых изображений, латентная интерполяция между первыми двумя тестовыми изображениями и сэмплирование из диагональных гауссиан по классам (
compute_activation_stats, по кодам обучающих изображений). - Ячейки 26–37: шумоподавляющий автоэнкодер (
add_noiseс множителем 0.1 по умолчанию, которая вызывается с 0.2 и при обучении, ячейка 31, и для тестовых изображений, ячейка 35;DenoisingBlockсDropout2d(0.3)), его t-SNE до и после обучения (ячейки 30 и 33), исходные, зашумлённые и очищенные тестовые изображения (ячейка 35) и сэмплирование по классам (ячейка 37). - Ячейки 38–49: автоэнкодер с KL-разреженностью (
kl_divergence_activations,SparseAE_KLс декодером на транспонированных свёртках, , ). Блоки его декодера тоже используют сигмоиды, и сам код пропускается через сигмоиду, поэтому гауссианы по классам в ячейке 49 подгоняются к кодам в . Картинка с разреженными активациями в ячейке 38 взята из Jordan (2018) — иллюстрированного введения в автоэнкодеры, которое рекомендует репозиторий. - Ячейки 50–59: автоэнкодер с L1-разреженностью (): обучение, t-SNE (ячейка 56), реконструкции (ячейка 57) и сэмплирование по классам (ячейка 59).
- Циклы обучения KL- и L1-моделей (ячейки 43 и 53) после прямого прохода запускают энкодер второй раз, чтобы получить активации для штрафа. Это удваивает вычисления энкодера, а в режиме обучения дважды за шаг обновляет скользящие средние батч-нормализации.
- Сохранённые значения функции потерь на обучении по эпохам (среднее по 937 батчам эпохи):
| Модель | Ячейка | Эпоха 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| обычная | 17 | 0.0103 | 0.0034 | 0.0026 | 0.0023 | 0.0021 |
| шумоподавляющая | 31 | 0.0313 | 0.0137 | 0.0116 | 0.0107 | 0.0102 |
| с KL-разреженностью (сумма двух слагаемых) | 43 | 8.8531 | 4.4430 | 2.3315 | 0.7877 | 0.2141 |
| с L1-разреженностью (сумма двух слагаемых) | 53 | 0.0095 | 0.0034 | 0.0028 | 0.0024 | 0.0022 |
Домашнее задание 1 из того же репозитория (нумерация ячеек та же) обучает автоэнкодер на CIFAR-10, а затем использует его энкодер для FID; часть о FID описана в заметке о FID. В ноутбуке нет сохранённых выходов, а ячейки для решений пусты, поэтому он документирует только процедуру.
- Ячейка 3:
set_seed(42)задаёт начальные значения (seed) генераторов случайных чиселrandomв Python, NumPy и PyTorch на CPU и всех GPU и устанавливаетcudnn.deterministic = Trueиcudnn.benchmark = False(что делает каждый из этих шагов). - Ячейки 6–13: CIFAR-10 (50 000 обучающих и 10 000 валидационных изображений размера , десять классов) с преобразованием
ToTensor(), за которым следуетx * 2 - 1, иdenormalize_image, , для показа. При обучении батчи по 128; валидационный загрузчик использует батчи из одного изображения. - Ячейки 15–19: студент пишет
EncoderиDecoderиз любыхConv2d,MaxPool2d,AvgPool2d,UpsampleиConvTranspose2d. Две проверки: развёрнутый код должен содержать меньше чисел, а декодер должен возвращать изображение . Входы лежат в , поэтому последнему слою декодера нужен или линейный выход, а не сигмоида. - Ячейки 20–22 и 32:
classification_score— описанный выше зондирующий классификатор. Функция переводит энкодер в режимeval(), вычисляет развёрнутый код каждого изображения подtorch.no_grad(), обучаетGradientBoostingClassifierсn_estimators=50иmax_depth=5на кодах 5000 случайных обучающих изображений и возвращает его долю правильных ответов на всех 10 000 валидационных изображениях. Ячейка 22 оценивает необученныйEncoder(); ячейка 32 требует, чтобы доля правильных ответов обученного энкодера была больше чем в 1.05 раза выше, чем у необученного. - Ячейки 23–31:
train(MSE, среднее значений функции потерь по батчам за один проход по обучающей выборке) иeval(то же на валидационной выборке, в режимеeval()и подtorch.no_grad());full_trainобучает модель сAdamWпри настройках по умолчанию (шаг обучения , затухание весов (weight decay) ) 30 эпох, строит графики MSE на обучении и валидации по эпохам, и её нужно доработать так, чтобы она загружала сохранённые веса вместо повторного обучения. - Ячейка 33: десять валидационных изображений и их реконструкции.
- Проблемы ноутбука.
classification_scoreпри каждом вызове выбирает новое случайное подмножество из 5000 обучающих изображений, поэтому необученный и обученный энкодеры проверяются на разных подмножествах; фиксированное подмножество сделало бы сравнение с порогом 5% менее шумным. Функция с именемevalв ячейке 27 перекрывает встроенную функцию Pythoneval. Ячейка 17 проверяет энкодер наtorch.rand(1, 3, 32, 32) - 1, то есть на значениях в , а не в ; проверяется только форма, так что вреда нет.
Дальше#
Обычный автоэнкодер выучивает полезные коды, но сам по себе не даёт способа сэмплировать новые изображения: он не задаёт априорного распределения кодов и никогда не учит произвольные точки латентного пространства декодироваться в правдоподобные изображения. Плотность, подогнанная к кодам после обучения, даёт своего рода генеративный конвейер, но гауссианы по классам выше потребовали меток и всё равно дали искажённые цифры. Следующая заметка делает распределение кодов частью модели и обучает её по нижней оценке логарифма правдоподобия, и это превращает автоэнкодер в генератор.
Литература#
- D. E. Rumelhart, G. E. Hinton, R. J. Williams. Learning internal representations by error propagation. In Parallel Distributed Processing, vol. 1, chapter 8, MIT Press, 1986; also ICS Report 8506, 1985. Раздел «The encoding problem».
- G. E. Hinton, R. R. Salakhutdinov. Reducing the dimensionality of data with neural networks. Science, 313(5786), 2006.
- P. Vincent, H. Larochelle, Y. Bengio, P.-A. Manzagol. Extracting and composing robust features with denoising autoencoders. ICML, 2008.
- P. Vincent, H. Larochelle, I. Lajoie, Y. Bengio, P.-A. Manzagol. Stacked denoising autoencoders: learning useful representations in a deep network with a local denoising criterion. JMLR, 11, 2010.
- P. Vincent. A connection between score matching and denoising autoencoders. Neural Computation, 23(7), 2011.
- A. Ng. Sparse autoencoder. CS294A lecture notes, Stanford, 2011.
- I. Goodfellow, Y. Bengio, A. Courville. Deep Learning. MIT Press, 2016. Раздел 7.1.2: L1-регуляризация; глава 14: автоэнкодеры.
- G. Loaiza-Ganem, J. P. Cunningham. The continuous Bernoulli: fixing a pervasive error in variational autoencoders. NeurIPS, 2019.
- L. van der Maaten, G. Hinton. Visualizing data using t-SNE. JMLR, 9, 2008.
- M. Wattenberg, F. Viégas, I. Johnson. How to use t-SNE effectively. Distill, 2016. Разделы «Cluster sizes in a t-SNE plot mean nothing» и «Distances between clusters might not mean anything».
- A. Saxe, P. W. Koh, Z. Chen, M. Bhand, B. Suresh, A. Ng. On random weights and unsupervised feature learning. ICML, 2011.
- G. Alain, Y. Bengio. Understanding intermediate layers using linear classifier probes. arXiv:1610.01644, 2016.
- J. T. Springenberg, A. Dosovitskiy, T. Brox, M. Riedmiller. Striving for simplicity: the all convolutional net. ICLR Workshop, 2015.
- A. Odena, V. Dumoulin, C. Olah. Deconvolution and checkerboard artifacts. Distill, 2016.
- A. L. Maas, A. Y. Hannun, A. Y. Ng. Rectifier nonlinearities improve neural network acoustic models. ICML Workshop on Deep Learning for Audio, Speech and Language Processing, 2013.
- A. Radford, L. Metz, S. Chintala. Unsupervised representation learning with deep convolutional generative adversarial networks. ICLR, 2016. Раздел 3: выход у генератора; раздел 4: изображения масштабированы в .
- J. Jordan. Introduction to autoencoders. Пост в блоге, 2018.
- Документация PyTorch:
ConvTranspose2d. - HSE-LAMBDA, репозиторий курса Deep Generative Models:
autoencoders.ipynbи01_hw_AE-and-FID.ipynb.