До сих пор у каждой картинки была метка: цифра, класс, маска. А если меток нет вовсе — можно ли всё равно нащупать в данных структуру? Автокодировщик отвечает так: пусть объект сам станет и вопросом, и ответом. Сеть обязана пропустить его через узкое горло короткого кода и восстановить обратно. Что уцелеет в этом сжатии — то и есть главное в объекте.

Объект как собственная метка

В уроке 31 цифра приходила с ответом — числом от нуля до девяти. Уберём ответ. Останется картинка 8×88\times8, то есть точка в 64-мерном пространстве. Автокодировщик состоит из двух частей. Энкодер сжимает вход в короткий код

z=fθ(x),zRm, m<64,z=f_\theta(x),\qquad z\in\mathbb R^{m},\ m<64,

а декодер разворачивает код обратно в картинку x^=gϕ(z)\hat x=g_\phi(z). Учат обе части вместе, минимизируя ошибку восстановления по всей выборке:

L(θ,ϕ)=1ni=1nxigϕ ⁣(fθ(xi))2.L(\theta,\phi)=\frac1n\sum_{i=1}^{n}\bigl\lVert x_i-g_\phi\!\bigl(f_\theta(x_i)\bigr)\bigr\rVert^2.

Метка нигде не участвует. Сигнал для обучения сеть добывает из самого объекта — поэтому автокодировщик относят к обучению без учителя.

Здесь прячется ловушка. Если код не уже входа и сеть достаточно гибкая, она выучит тождество: просто перепишет вход в выход, ничего не поняв. Низкая ошибка тогда не значит ничего. Значит, сжатие обязано быть настоящим — код должен быть короче, чем объект.

Насколько именно грубее? Посчитаем среднеквадратичную ошибку восстановления по всем 1797 цифрам для каждой длины кода. Кривая падает круто, а рядом растёт доля сохранённой дисперсии — сколько разброса данных объясняет код.

Рисунок шире экрана — проведите по немуОткрыть целиком ↗
Две кривые от длины кода. Красная — ошибка реконструкции MSE — падает с 0,062 при коде 1 почти до нуля при коде 64. Синяя пунктирная — сохранённая дисперсия в процентах — растёт с 15 процентов до 100. Отметка: почти всё восстановлено уже при 16 из 64
Рис. 35.2. Короче код — грубее восстановление

Ошибка восстановления против длины кода. Уже 16 чисел из 64 удерживают около 85% дисперсии и почти всю узнаваемость. Полный код длины 64 восстанавливает точно — и ничего не сжимает.

Обрати внимание на крайние точки. Код длины 64 не сжимает ничего и восстанавливает идеально — но пользы в нём ноль. Код длины 1 сжимает сильнее всего, а цифру узнать нельзя. Всё интересное — посередине, где сжатие уже ощутимо, а смысл ещё цел.

Латентное пространство: карта, которую никто не рисовал

Сожмём каждую цифру до двух чисел и разложим точки на плоскости. Меток при построении кода не было — и всё же одинаковые цифры садятся рядом, образуя острова.

Рисунок шире экрана — проведите по немуОткрыть целиком ↗
Облако из почти 1800 точек в двумерном коде, раскрашенных по истинной цифре от 0 до 9. Нули, единицы, тройки, шестёрки образуют отдельные сгустки по краям; в центре классы перемешаны. Подпись: цвет — истинная цифра, но код построен без единой метки
Рис. 35.3. Двумерный код: цифры сами собираются в группы

Каждая точка — цифра, сжатая до двух чисел. Цвет добавлен потом, для проверки; сам код метки не видел. Группы проступают там, где почерк устойчив, и смешиваются там, где цифры похожи.

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

Кластеры, которые никто не размечал

Насколько эта структура настоящая? Запустим по цифрам kk-средних с десятью кластерами — снова без единой метки. Затем спросим постфактум: какая цифра преобладает в каждом кластере. Доля «своих» в кластерах — чистота — выходит около 0,790{,}79. Почти четыре цифры из пяти сгруппированы верно, и никто не подсказывал ответ.

Линейный автокодировщик — это метод главных компонент

Не случайно наши картинки так похожи на PCA. Есть точное утверждение.

Поэтому линейный автокодировщик не открывает ничего сверх PCA — зато даёт ей понятный вид «сжал и восстановил».

Нелинейный код гнётся под данные

Настоящая сила автокодировщиков начинается там, где энкодер и декодер нелинейны. Линейный код умеет только проектировать облако точек на плоское подпространство — поэтому он и совпадает с PCA. Но реальные цифры лежат не на плоскости: тройки, восьмёрки и девятки образуют искривлённое многообразие в 64-мерном пространстве. Нелинейная сеть строит изогнутую поверхность, повторяющую эту форму, и тем же числом координат описывает данные точнее. Именно об этом выигрыше и говорили Хинтон с Салахутдиновым: многослойный автокодировщик находит коды, из которых восстановление получается заметно лучше, чем из главных компонент.

Русская линия здесь прямая. То, что на Западе называют разложением Карунена—Лоэва и главными компонентами, в советской теории случайных процессов развивалось как каноническое разложение В. С. Пугачёва: случайную функцию раскладывают по некоррелированным составляющим, и первые из них удерживают наибольшую долю разброса. Автокодировщик с бутылочным горлом делает ровно то же — оставляет несколько самых информативных направлений и выбрасывает остальное.

Ошибка восстановления как детектор странного

У сжатия есть неожиданное применение. Обучим автокодировщик только на обычных объектах. Тогда привычное он восстанавливает хорошо, а необычное — плохо, потому что для него в коротком коде не нашлось места. Величину

s(x)=xx^s(x)=\lVert x-\hat x\rVert

называют оценкой аномальности: чем хуже восстановление, тем подозрительнее объект.

::::

Шум как учитель: деноизирующий автокодировщик

Есть другой способ запретить сети лениться. Подадим на вход испорченную картинку x~\tilde x, а целью оставим чистую xx:

x~  z  x^x.\tilde x\ \longrightarrow\ z\ \longrightarrow\ \hat x\approx x.

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

::::

Как проверить, что код полезен

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

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

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

Что забрать с собой

Автокодировщик превращает объект в собственную метку и заставляет информацию протиснуться через узкое горло. Что уцелело в коротком коде — то и было главным. Линейный случай — это в точности PCA и каноническое разложение Пугачёва; нелинейный ложится на изогнутую поверхность данных. Из одной идеи вырастают сразу три инструмента: сжатие, поиск аномалий по ошибке восстановления и деноизинг. И над всеми висит одно предупреждение — низкая ошибка сама по себе не доказывает, что код осмыслен; это проверяют внешней задачей. А за словами «главные направления» и «поворот осей кода» скрывается язык линейной алгебры: дальше мы разберём матрицы как преобразования пространства и увидим, откуда берутся сами эти направления.

Длина кода, шум и восстановление цифры

Загружается живая иллюстрация…

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

Задачи