Автоенкодери й VAE

Мережу можна змусити стиснути зображення у вісім чисел і розгорнути назад. Звучить як архіватор, а виявляється першим кроком до генерації. Але щойно ми питаємо «а наскільки добре воно малює?», під ногами зникає ґрунт: метрики, якій можна вірити, тут немає. Ця тема про те, як її будують і як перевіряють перед тим, як їй повірити.

Усі тридцять шість попередніх тем мережа щось розпізнавала: казала клас, малювала рамку, фарбувала маску, міряла глибину. На вході було зображення, на виході — відповідь, і відповідь була коротшою за вхід. Тепер повернемо стрілку. Вхід — коротке, вихід — зображення. Мережа має не описати картинку, а намалювати її.

Найпростіший спосіб підійти до цього — навіть не намагатися генерувати. Візьмемо мережу й попросимо її зробити щось безглузде на вигляд: віддати на виході рівно те, що прийшло на вхід. Тотожність. Задача, яку розвʼязує оператор присвоєння.

ГорлоСтиснути, щоб зрозуміти

Безглуздою вона лишається рівно доти, доки ми не поставимо посередині пляшкове горло (bottleneck) — шар із дуже малою кількістю чисел. Тоді зображення 28×28, тобто 784 числа, мусить пролізти крізь горло з восьми — і аж тоді задача стає осмисленою. Пройти крізь горло можна, тільки викинувши все зайве, а «зайве» мережа мусить визначити сама.

Мережа з таким горлом зветься автоенкодером (autoencoder) і складається з двох половин:

енкодер

Стискає зображення до восьми чисел. У нас це дві згортки з кроком 2 (28×28 → 14×14 → 7×7) і лінійний шар. Ті вісім чисел звуться латентним кодом (latent code), а простір, у якому вони живуть, — латентним простором.

декодер

Розгортає вісім чисел назад у 28×28. Дзеркало енкодера: лінійний шар і дві транспоновані згортки. Це та сама будова, що в U-Net із теми 30, тільки без скіп-зʼєднань — бо саме вони дозволили б обійти горло, а нам потрібно, щоб обійти було неможливо.

Транспонована згортка (ConvTranspose2d) робить те саме, що звичайна згортка з кроком 2, тільки навпаки: та зменшувала сторону вдвічі, ця вдвічі її збільшує. Кожен вхідний піксель множиться на ядро й «розсіюється» на кілька сусідніх вихідних, а там, де розсіяне від сусідів накладається, значення додаються — саме так із карти 7×7 виходить 14×14.

втрата

Порівнюємо вихід із входом попіксельно. Середній квадрат різниці — і все. Ані міток, ані розмітки, ані людини. Це різновид самонаглядового навчання з теми 16, і привід тут найпростіший з можливих: відповіддю є сам вхід.

Куди дівається зображення дорогою до восьми чисел

Той самий шлях, що описаний вище словами. Під кожним кроком стоїть розмір того, що з нього виходить; горло намальоване вузьким навмисне — саме воно змушує мережу вибирати, що лишити.

Два числа тут головні: 784 на краях і 8 посередині. Стиснення у 98 разів — і жодного шляху в обхід. Дорога назад дзеркальна, тільки згортки з кроком 2, які вдвічі зменшували сторону, замінені на транспоновані, які її вдвічі збільшують. Порівнювати вихід із входом можна прямо, бо це два зображення однакового розміру: у цьому й уся втрата.

Наскрізний приклад теми — той самий набір, що й у решті блоку: шість фігур 28×28 (коло, квадрат, ромб, кільце, хрест, трикутник), центр гуляє на пʼять пікселів у будь-який бік, радіус від пʼяти до восьми, зверху гаусів шум 0.06. Автоенкодер з латентом 8 має 16 769 ваг — менше, ніж пікселів у двадцяти двох картинках.

Куди похибка не спуститься. Шум у наших зображеннях невідновний за побудовою: навіть ідеально вгадавши форму, центр і радіус, мережа не вгадає конкретний візерунок шуму. Ми заміряли цю межу — вона дорівнює 0.00180. Усі числа нижче читай відносно неї, а не відносно нуля.

ШиринаСкільки чисел треба фігурі

Наскільки вузьким має бути горло? Питання не риторичне: у ньому весь сенс конструкції. Занадто широке горло — і мережа просто перепише вхід на вихід, нічого не зрозумівши. Занадто вузьке — і вона фізично не встигне вкласти туди форму.

Порахуємо спершу руками, скільки інформації в наших фігурах узагалі є. Шість класів, одинадцять положень центра по вертикалі, одинадцять по горизонталі, чотири радіуси: 6 × 11 × 11 × 4 = 2904 різні фігури. Щоб занумерувати їх усі, досить дванадцяти двійкових розрядів — бо 212 = 4096, а це більше за 2904. Вісім дійсних чисел вміщають незрівнянно більше, тож із погляду самої лише ємності горло в нас із величезним запасом.

Але ємність — не те саме, що досяжність. Ми навчили автоенкодери з латентом 1, 2, 4, 8, 16 і 32, по три зерна на кожну ширину, і подивились, що дає кожне подвоєння.

Що дає кожне подвоєння горла

Ліворуч — та сама фігура, відновлена автоенкодером цієї ширини. Праворуч — похибка відновлення на всіх шести ширинах; пунктир — шумова підлога 0.00180, нижче якої не буває.

похибка—
розкид по зернах—
виграш від подвоєння—
Де злам — і що при цьому видно на картинці. Перші три подвоєння знімають по 28-35 % похибки кожне, далі виграш зривається більш ніж удвічі й далі тримається на місці: 8 → 16 дає 11.3 %, 16 → 32 дає 10.6 %. А тепер подивись ліворуч: на вході кільце з чіткою діркою, на виході — пляма, і так при усіх шести ширинах. Положення й розмір мережа взяла, форму — ні.

Злам виразний, і спокуса прочитати його просто: мовляв, форма вклалась у вісім чисел, а далі йдуть дрібниці. Картинка ліворуч цю спокусу знімає. Наш пробний приклад — кільце, тобто фігура з діркою посередині, і жодна з шести ширин цієї дірки не відтворила. Навіть при латенті 32.

Прочитаймо тоді числа чесніше. Похибка спиняється на 0.02472, а шумова підлога — 0.00180: ми зупинились майже в чотирнадцять разів вище за межу. Отже впертися ми могли в що завгодно, тільки не в ємність горла — 2904 варіанти фігури вклалися б у дванадцять двійкових розрядів, а в нас їх вісім дійсних чисел. Уперлись ми в мережу: маленький декодер із двох транспонованих згорток за 12 епох навчився ставити пляму потрібного розміру в потрібне місце, і на цьому спинився.

Практичний висновок, який коштує дорого. Коли автоенкодер відновлює погано, першою думкою буває «горло вузьке, розширмо». Наш замір показує, що це часто не так: між латентом 8 і 32 похибка впала на пʼяту частину, а дірка в кільці не зʼявилась на жодній ширині. Розширення горла лікує лише брак місця, а не брак сили декодера — і перевіряти, у що саме ти вперся, треба до того, як міняти архітектуру.

Зверни увагу на розкид при латенті 1: ±0.02644 при середньому 0.09612. Це майже третина від значення, і три зерна дали 0.13350, 0.07845 і 0.07642. Одне число тут вирішує, у яку канаву впаде навчання, і саме тому міряти на одному зерні не можна. При латенті 16 розкид уже ±0.00087 — навчання стабільне.

СуддяСуддя, який завжди відповідає

Тепер найцікавіше. Автоенкодер уміє відновлювати. А чи вміє він малювати нове? Щоб відповісти, треба спершу навчитися це питання ставити числом — і ось тут починається справжня тема.

Перша ідея очевидна: візьмемо окремий класифікатор шести фігур, навчений на справжніх даних, і покажемо йому те, що намалювала модель. Назвемо його суддею. Якщо суддя впевнено каже «це квадрат» — мабуть, квадрат і намальовано.

Суддя влаштований звичайно: два згорткові блоки й лінійна голова. На справжніх фігурах він дає точність рівно 1.0000 із середньою впевненістю 0.9914 — задача для нього тривіальна, а отже будь-яка його невпевненість на згенерованому щось означає.

Так здається. Насправді ні, і причину видно з будови задачі, ще до першого заміру.

У судді немає сьомого варіанта. Softmax завжди сумується в одиницю, і найбільша з шести ймовірностей ніколи не буває меншою за одну шосту. Суддя навчений відповідати на питання «яка це з шести фігур», а не на питання «чи це взагалі фігура». Друге питання йому не ставили, і відповідати на нього він не вміє — але промовчати теж не може.

КалібруванняСпершу калібруй вимірювач

Метрика генерації — не термометр, перевірений на заводі. Це саморобний прилад, який ми щойно спаяли самі. Перш ніж вірити його показанням на невідомому, треба подивитись, що він показує на завідомо поганому.

Ми зібрали вісім наборів по 600 зображень, кожен зіпсований по-своєму, і пропустили їх крізь чотири кандидати в метрики. Ось ці кандидати:

КандидатЩо рахуєЛогіка
впевненість суддісередня найбільша ймовірністьгарний зразок суддя впізнає впевнено
покриття класівскільки з шести фігур набрали хоч 1 % вибіркимодель має малювати всі фігури, а не одну
зразок → найближча справжнядля кожного зразка шукаємо найсхожішу справжню фігуру й міряємо відстань до неїзразок має бути схожий хоч на щось справжнє
справжня → найближчий зразокдля кожної справжньої фігури шукаємо найсхожіший зразок і міряємо відстань до ньогозразки мають покрити все розмаїття, а не куточок

Дві останні потребують одного уточнення: відстань тут — відстань між двома картинками. Ми віднімаємо одну від одної піксель за пікселем і дивимось, наскільки великою вийшла різниця; нуль означає точний збіг, а що більше пікселів розійшлось і що сильніше — то число більше. Одиниць у нього немає, тож саме по собі воно нічого не каже: сенс має тільки порівняння з тим, що дають справжні фігури. Саме тому першим рядком таблиці калібрування стоїть «справжні, інша вибірка» — це й є та риска, відносно якої читаються всі інші числа.

Що показує вимірювач на завідомо поганому

Обери набір — і подивись, як поводиться кожен із чотирьох кандидатів. Риска на смужках — те, що дають справжні фігури.

що подаємо судді
впевненість—
класів із 6—
зразок → спр.—
спр. → зразок—
Головне в цій фігурі. Прожени всі девʼять положень і подивись на першу смужку. Усе біле дає впевненість 0.9997 — більше, ніж справжні фігури з їхніми 0.9913. Усе чорне дає 0.9925 — теж більше. Лише кола — це колапс мод (mode collapse): модель уміє малювати лише одну фігуру з шести й повторює її знову й знову. Тут він дає 0.9777, і це більше, ніж будь-яка робоча модель у цій темі.

Дві метрики з чотирьох провалились, і кожна по-своєму.

Впевненість судді провалилась повністю. Біле полотно не містить жодної фігури, і суддя впевнений у ньому більше, ніж у справжньому колі. Це не поганий суддя й не помилка навчання — це прямий наслідок того, що в нього немає варіанта «нічого з переліченого». Біле полотно просто лежить дуже далеко від межі між класами, а softmax тим упевненіший, чим далі точка від межі.

Відстань «зразок → найближча справжня» провалилась теж, і це менш очевидно. У наборі «лише кола» вона дорівнює 4.3182, а в справжніх фігур — 4.4583. Тобто повний колапс мод виглядає за цією метрикою краще за справжні дані. Логіка прозора: якщо малювати самі кола, то кожне намальоване коло справді дуже схоже на якесь справжнє коло з полиці. Метрика міряє якість окремого зразка й за побудовою сліпа до того, що всі зразки однакові.

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

ПараПара, яка працює

Два кандидати, що лишились, поодинці теж не годяться — але разом закривають один одного.

Покриття класів ловить колапс: «лише кола» дає 1, «кола + квадрати» дає 2 замість шести. Але воно сліпе до якості. Подивись на «чистий шум»: суддя розкидав випадкові плями по чотирьох класах, і покриття чесно показало 4.

Відстань «справжня → найближчий зразок» ловить решту. Вона питає інше: чи для кожної справжньої фігури знайдеться схожий зразок. У колапсі не знайдеться — для справжнього кільця немає жодного схожого кола, і число росте з 4.3831 у справжніх до 5.0618. У шумі воно злітає до 14.8129.

набірвпевненістькласівзразок → спр.спр. → зразок
справжні, інша вибірка0.991364.45834.3831
лише кола0.977714.31825.0618
кола + квадрати0.982724.06395.0592
розмита каша0.626435.10705.5796
середнє всіх фігур0.725315.25237.8265
усе чорне0.992516.610210.0414
чистий шум0.8233415.178614.8129
усе біле0.9997123.622525.4007

Правило теми звідси таке: модель добра тоді, коли обидва числа пари добрі. Високе покриття при великій зворотній відстані означає різноманітне сміття. Мала зворотна відстань при низькому покритті означає охайний колапс. Кожен із восьми зіпсованих наборів провалюється хоча б за одним числом — і таблиця вище показує це рядок за рядком.

Чесні межі цієї пари, обидві. «Розмита каша» стоїть гірше за найкращу модель теми за обома числами — 3 класи проти 4.33 і 5.5796 проти 5.2020. А от «лише кола» ловиться лише покриттям: його зворотна відстань 5.0618 навіть трохи менша за 5.2020 в робочої моделі, бо 600 кіл усіх розмірів і положень таки знаходять щось схоже для кожної справжньої фігури. Друга межа неприємніша: каша стоїть краще за автоенкодер (5.5796 проти 7.1035 при однаковому покритті). Це не збій метрики, а її відповідь — розмиті справжні фігури ближчі до справжніх фігур, ніж те, що автоенкодер малює з випадкового коду. Наступний розділ пояснить, чому це саме так.

І ще один доказ, що впевненість судді треба викинути. Ми навчили трьох суддів — на 10, 20 і 40 епохах — і показали їм ті самі зображення. Змінюється лише те, хто дивиться.

суддяточністьсправжніавтоенкодерVAE β=0.2лише колачистий шум
10 епох0.93170.77750.75930.62000.56110.6351
20 епох0.99500.96100.83710.71820.91910.6905
40 епох1.00000.99140.89220.79210.97770.8233

Впевненість на колапсі виросла з 0.5611 до 0.9777 — у той час, як самі зображення не змінилися ані на піксель. Отже це число описує не зразки, а суддю. Довше навчений класифікатор просто гостріший, і гострий він однаково на всьому: і на фігурах, і на сміттях.

ПорожнечаЗвідки беруть z

Тепер повернімось до питання, заради якого все це будувалось. Щоб намалювати нову картинку, ми не подаємо на вхід нічого. Ми беремо вісім чисел з повітря й просимо декодер їх намалювати. «З повітря» на практиці означає з нормального розподілу: кожен вимір тягнемо з N(0, 1).

Питання, яке тут пропускають майже завжди: а чи лежать у тому місці коди справжніх зображень? Порахуємо руками. Довжина вектора з восьми стандартних нормальних чисел у середньому близька до кореня з восьми, тобто до 2.83. Тепер подивимось, де насправді лежать коди автоенкодера.

Куди ми цілимось, тягнучи z із N(0, 1)

Ліворуч — у масштабі: кулька, з якої ми беремо z, і хмара, де насправді лежать коди. Праворуч — вісім зразків, які видав декодер.

модель
довжина коду—
кодів у кулі 5.66—
класів із 6—
спр. → зразок—
Ось де дірка. Коди автоенкодера лежать на середній відстані 30.20 від нуля, а ми тягнемо z із кулі радіуса 2.83. У кулю подвоєного радіуса не потрапляє жодного справжнього коду — 0.0 %. У VAE туди потрапляють усі 100 % при будь-якому β.

Це і є відповідь на питання, чому автоенкодер не є генератором, і вона не метафорична. Ми питаємо декодер про точку, у якій він ніколи не навчався. Під час навчання на вхід декодера приходили вектори довжиною близько тридцяти; ми подаємо йому вектор довжиною три. Що він намалює — не визначено нічим.

Слово «дірка» тут навіть занадто мʼяке. Це не проміжок між двома кластерами кодів — це зовсім інша область простору, куди навчання не заглядало.

Чому так виходить саме собою. Втрата автоенкодера ніяк не обмежує довжину коду. Мережі вигідно рознести коди якнайдалі один від одного: чим далі, тим легше декодерові їх розрізнити й тим менша похибка відновлення. Ніщо не тягне їх до нуля, і вони розповзаються. Наш автоенкодер має розмах кодів 6.77 при тому, що в VAE з β=0.2 він 1.10. Розмах кодів — це наскільки широко розкидані самі числа коду: у стандартного нормального розподілу, з якого ми беремо z, він дорівнює одиниці, тож 1.10 означає «майже точно те, що треба», а 6.77 — «розповзлось майже всемеро ширше».

ХмаркаХмарка замість точки

Полагодити це можна одним рухом: змусити коди жити там, звідки ми потім братимемо z. Не «десь поруч», а саме там — у стандартному нормальному розподілі.

Спосіб, який для цього придумали, зветься варіаційним автоенкодером, або просто VAE. Ідея на один абзац: енкодер видає не точку, а хмарку. Замість восьми чисел він видає шістнадцять — вісім центрів mu й вісім ширин sigma, по одній парі на вимір. Кодом зображення стає не одна точка, а цілий розподіл навколо неї.

код зображення = N(mu, sigma²), а не одна точка

З цього одразу випливають дві речі. По-перше, декодерові тепер щоразу показують трохи іншу точку з тієї самої хмарки, тож він мусить навчитися малювати одне й те саме з цілого околу — а не з єдиної точки. Порожнеч між кодами стає менше просто тому, що кожен код займає обʼєм, а не нуль.

По-друге — і це головне — тепер є за що вхопитися, щоб притягнути коди до нуля. Хмарку можна порівняти зі стандартним нормальним розподілом і покарати за відхилення. Ця відстань між розподілами зветься розбіжністю Кульбака — Лейблера й у нашому випадку рахується формулою:

KL = −½ · Σ (1 + log sigma2 − mu2 − sigma2)

Розберімо її по частинах, бо вона виглядає страшніше, ніж є.

Σ означає «додай по всіх вимірах»

Вимірів у нас вісім, тож усередині дужок рахується вісім однакових виразів і всі складаються. Ніякої іншої математики в цьому символі немає.

доданок mu² тягне центр до нуля

Чим далі центр хмарки від нуля, тим більше mu², тим більший штраф. Це прямо те, чого нам бракувало в автоенкодері.

пара log sigma² і sigma² тримає ширину коло одиниці

Якщо хмарка надто вузька, sigma² мале, і log sigma² іде в мінус нескінченність — штраф величезний. Якщо надто широка, великим стає сам sigma². Мінімум цієї пари припадає рівно на sigma = 1.

одиниця й мінус одна друга — стала

Вони потрібні лише для того, щоб при mu = 0 і sigma = 1 весь вираз дорівнював нулю. Підстав ці значення: log 1 = 0, і в дужках лишається 1 + 0 − 0 − 1 = 0. Штрафу немає — саме те, чого ми хотіли.

Тобто KL — це один вимірювач: наскільки хмарка, яку видав енкодер, не схожа на стандартну. Нуль означає «точнісінько така, як треба».

Що саме штрафує KL

Ліворуч — та сама пара чисел, яку тепер видає енкодер: замість однієї точки центр і ширина. Праворуч — сам штраф, порахований за формулою вище: окремо як функція центра (при ширині 1) і окремо як функція ширини (при центрі 0).

Обидві криві мають мінімум там, де ми його й хотіли. За центром — у нулі, і чим далі центр, тим швидше росте штраф. За шириною — рівно в одиниці, і росте він в обидва боки: карається і надто вузька хмарка, і надто широка. Разом ці дві криві й означають «будь схожа на N(0, 1)» — більше в цій формулі нічого немає.

ХитрістьРепараметризація

Лишилось одне місце, і воно тонке. Декодерові треба одна точка з хмарки. Здавалося б, візьми та й насемпли.

Не вийде — і причина не в тому, що це складно, а в тому, що після цього мережа перестане навчатися. Згадаймо, як узагалі працює навчання: ми рахуємо втрату, потім похідну втрати по кожній вазі, потім зсуваємо ваги проти градієнта. Щоб градієнт дійшов до енкодера, кожен крок від ваги до втрати має бути записаною операцією, яку можна продиференціювати.

А вибірка з розподілу такою операцією не є. Витягнеш число зі свого генератора, запишеш його в тензор — і звʼязок обірвано: результат ніяк не памʼятає, що центр хмарки був 1.5, а не 1.4. Перевірити це просто: у такого тензора requires_grad дорівнює False, а графа обчислень немає взагалі. Похідної не існує не тому, що її важко порахувати, — її немає що рахувати.

Хитрість, яка це розвʼязує, зветься репараметризацією (reparameterization trick) й полягає в тому, щоб винести випадковість убік:

z = mu + sigma · eps,   де eps береться з N(0, 1)

Подивись, що змінилось. Випадкове число eps тепер тягнеться окремо й не залежить ані від mu, ані від sigma. А сам z виходить із них звичайними додаванням і множенням — операціями, які диференціюються тривіально. Похідна z по mu дорівнює одиниці, похідна по sigma дорівнює eps. Градієнт проходить наскрізь.

Розподіл при цьому не змінюється: якщо eps стандартне нормальне, то mu + sigma·eps має рівно те саме нормальне з центром mu й шириною sigma. Ми не підмінили задачу, ми лише інакше її записали.

Пастка, яку варто запамʼятати. Виклик torch.normal(mu, sigma) виглядає бездоганно: у результату є grad_fn, тобто граф обчислень будується. Але похідна по mu в нього дорівнює нулю. Градієнт проходить крізь цей вузол і не приносить нічого. Це найгірший вид відмови: код працює, навчання йде, енкодер мовчки не вчиться. Правильні імена — наша формула руками або Normal(mu, sigma).rsample(), де r означає рівно reparameterized; звичайний sample() градієнта не має взагалі.

Обійтися без репараметризації теоретично можна: існує оцінка градієнта через логарифм щільності, і вона незміщена, тобто в середньому правильна. Питання не в правильності, а в розкиді.

Чому обхідний шлях не годиться

Задача, де відповідь відома точно: похідна середнього від z² по mu дорівнює 2·mu, тобто при mu = 1.5 рівно 3.0. Обидві оцінки в середньому правильні — подивись на ширину.

похибка через eps—
похибка обхідна—
скільки зразків потрібно обхідному, щоб зрівнятись—
Ціна обхідного шляху. Розкид на один зразок: 1.4010 через репараметризацію проти 6.8485 через логарифм щільності — у 4.9 раза більше. Похибка середнього падає як корінь із кількості зразків, тож щоб зрівнятися, обхідній оцінці потрібно приблизно в 24 рази більше зразків.

Двадцять чотири рази більше зразків на кожному кроці навчання — це не уточнення, це вирок. Саме тому VAE став практичним лише разом із репараметризацією, а не раніше.

ВагиДві половини втрати й що робить β

Тепер зберімо втрату VAE. У ній рівно дві половини, і вони тягнуть у різні боки:

втрата = похибка відновлення + β · KL / 784 ↑ ↑ «намалюй точно» «лежи там, звідки я потім братиму z»

Перша половина хоче, щоб код ніс якнайбільше інформації про конкретне зображення. Друга хоче, щоб код був схожий на випадковий шум. У межі друга виграє повністю — усі коди сходяться в нуль, декодер малює одне й те саме, і KL дорівнює нулю. Це відомий провал, який зветься колапсом апостеріорного розподілу (posterior collapse): «апостеріорний» тут означає той самий розподіл, який енкодер видає після того, як побачив зображення, — тобто нашу хмарку. Колапс — це коли хмарка перестає залежати від зображення взагалі.

Множник β вирішує, кого слухати. Ще одна деталь, яку часто ховають: похибка відновлення в нас — середнє по 784 пікселях, а KL — сума по восьми вимірах латента. Щоб β означало те саме, що в статтях, KL ділиться на 784: тоді обидві половини стають «на один піксель». Без цієї домовленості число β не означає нічого, і порівнювати його з чужою роботою не можна.

βвідновленнярозмах кодівкласівспр. → зразок
0 (автоенкодер)0.021296.773.007.1035
0.20.022601.104.335.2020
10.031680.783.675.1762
40.051030.433.005.4614

Компроміс видно в двох перших стовпчиках: від β=0.2 до β=4 похибка відновлення росте з 0.02260 до 0.05103, а розмах кодів падає з 1.10 до 0.43. Хмарки стискаються до прописаної їм одиничної ширини — рівно те, чого KL і вимагає.

Але четвертий стовпчик суперечить очікуванню, і це варте окремої уваги. Логіка «більше β — щільніший латент — краще покриття» тримається лише до певної межі. Після неї друга половина втрати вже не впорядковує латент, а псує декодер: при β=4 похибка відновлення 0.05103, тобто в 2.4 раза гірша за автоенкодерну, і декодер малює розмиті плями. Суддя такі плями розкладає по двох-трьох класах, і покриття падає.

У нашому замірі найкраще генерує β=0.2: покриття 4.33 — найбільше з чотирьох моделей. Зворотна відстань у нього 5.2020 проти 5.1762 у β=1, але купи по зернах тут перетинаються ([5.1798, 5.1864, 5.2398] проти [4.8983, 4.9411, 5.6893]), тож різниці між ними немає. Головне інше: найкращий β — проміжний, а не найбільший.

РозбіжністьХто виграє, залежить від метрики

Тепер головна таблиця теми. Чотири моделі з однаковим кістяком і латентом 8, по три зерна кожна, пʼять чисел на кожну.

модельвідновленнявпевненістькласівзразок → спр.спр. → зразок
автоенкодер0.021290.82393.008.13557.1035
VAE β=0.20.022600.79614.336.08505.2020
VAE β=10.031680.77853.674.77475.1762
VAE β=40.051030.73843.004.79875.4614

Візьми поглядом два рожеві рядки — автоенкодер і β=4 — і прочитай їх у двох стовпчиках. За похибкою відновлення автоенкодер кращий у 2.4 раза. За зворотною відстанню кращий β=4. Одні й ті самі дві моделі, дві метрики, протилежне впорядкування.

І це не гра середніх. Купи по трьох зернах не перетинаються в обох випадках, а за критерієм курсу (тема 26) саме це є доказом справжньої різниці:

метрикаавтоенкодер, три зернаVAE β=4, три зернакупи
відновлення0.02002 · 0.02050 · 0.023360.04757 · 0.04906 · 0.05646не перетинаються
спр. → зразок6.7896 · 6.9806 · 7.54025.1295 · 5.2800 · 5.9745не перетинаються

Переможець змінюється разом із метрикою

Обери метрику — і подивись, хто виявиться найкращим. Крапки — окремі зерна, смуга — середнє.

чим міряємо
переможець—
його число—
Шість метрик — чотири різні переможці. Похибка відновлення обирає автоенкодер, впевненість судді обирає його ж, покриття класів обирає β=0.2, обидві відстані обирають β=1, а розмах кодів — β=4. Жодна з них не бреше: вони просто питають різне. Перевір ще одну річ — у скількох положеннях перемога взагалі виходить за межі розкиду по зернах.

Особливо повчальний стовпчик впевненості. Він ставить автоенкодер першим (0.8239) — тобто найгіршу з чотирьох моделей за генерацією називає найкращою. Але чесність вимагає додати: розкид тут ±0.0493, і купи по зернах перетинаються з усіма трьома іншими моделями. Тобто метрика, яка впорядковує неправильно, ще й не показує жодної доведеної переваги: її не можна ані підтвердити, ані спростувати — і саме тому нею не користуються.

ШляхШлях між двома кодами

Класична демонстрація автоенкодерів виглядає так: беруть коди двох різних зображень, зʼєднують відрізком і малюють те, що виходить у проміжних точках. Підручникова обіцянка: у VAE середина осмислена, а в автоенкодері — привид із двох фігур, накладених одна на одну.

Ми цю обіцянку перевірили числом, і вона не підтвердилась. Розкажемо чесно, як міряли.

що робимо

Беремо 40 пар тестових зображень різних класів, зʼєднуємо їхні коди відрізком і йдемо по ньому девʼятьма кроками.

чим міряємо

Розрив — відстань від точки шляху до найближчого справжнього коду, поділена на типову відстань між сусідніми кодами. Ділення обовʼязкове: коди автоенкодера живуть на масштабі 3.701, а коди VAE — на масштабі 0.675, і без нормування числа непорівнянні взагалі.

що вийшло

У середині шляху розрив дорівнює 1.75 кроку сітки в автоенкодера й 1.69 у VAE. Різниця — шість сотих кроку.

Іти від однієї фігури до іншої

Веди повзунок від однієї фігури до другої й дивись, що декодер малює по дорозі. Внизу — розрив у кожній із девʼяти точок шляху.

модель
розрив—
сірих пікселів—
Замір, який не підтвердив підручник. Крива розриву однакова в обох моделей: 1.75 проти 1.69 у найвищій точці. Частка сірих пікселів у середині навіть більша у VAE — 0.102 проти 0.079. Відрізок між двома справжніми кодами однаково порожній в обох.

Чому так вийшло — зрозуміло, якщо згадати попередні розділи. Декодер — це гладка функція, і будь-яка точка латента щось малює. Різниця між автоенкодером і VAE не в тому, що між двома справжніми кодами по-різному порожньо. Різниця в тому, де ці коди лежать: у VAE — там, звідки ми потім тягнемо z, а в автоенкодера — за тридцять одиниць від того місця.

Що з цього забрати як метод. Класична демонстрація показує відрізок між двома справжніми кодами — а зламано в автоенкодера геть інше місце. Демонстрація виглядає переконливо, дивиться не туди, і на наших даних дає нуль різниці. Це не привід не вірити підручникам; це привід міряти твердження, а не переказувати.

КартаКарта латентного простору

Лишилось подивитись, що взагалі опинилось у тих восьми числах. Вісім вимірів намалювати не можна, тож стиснемо їх у два методом головних компонент: шукаємо той напрямок у восьмивимірному просторі, уздовж якого точки розкидані найсильніше, потім другий такий напрямок, перпендикулярний до першого, — і малюємо на площині тіні всіх точок на цю пару напрямків. Тіні зберігають те, чим точки різняться найбільше, і гублять решту; підпис під картинкою каже, яку частку розкиду ці два напрямки втримали. Фарбуємо точки за справжніми класами — тими самими, яких мережа не бачила жодного разу.

Число «сусіди дають клас» під картинкою рахується не по цих двох напрямках, а по всіх восьми: беремо кожну точку, знаходимо найближчі до неї коди інших зображень і питаємо, чи збігається їхній клас із її власним. Якщо латент нічого про клас не знає, збігів буде стільки, скільки дасть випадкове вгадування, — одна шоста.

Що групується в латенті насправді

Перемкни розфарбування — і подивись, яка ознака дає охайніші купи: клас фігури чи те, у якій чверті кадру вона стоїть.

модель
розфарбувати за
сусіди дають клас—
сусіди дають чверть—
Несподіванка, яку варто назвати. Клас фігури сусіди в латенті вгадують на 0.6900 при рівні вгадування 0.1667 — тобто клас там є. Але чверть кадру вони вгадують на 0.9767 при рівні 0.2500. Латент описує насамперед де стоїть фігура, а не яка вона.

Це чесніша картинка, ніж звична ілюстрація з охайними кольоровими островами. Автоенкодер не має жодних підстав вважати клас важливішим за положення: обидва однаково потрібні, щоб відновити зображення. У наших даних центр гуляє на пʼять пікселів у кожен бік, і це просто більша частина мінливості, ніж вибір однієї фігури з шести.

Висновок звідси практичний і переноситься далеко за межі теми: латент кодує те, що потрібно для відновлення, а не те, що цікаво тобі. Якщо потрібен латент, у якому групується саме клас, доведеться або прибрати мінливість положення з даних, або додати в задачу щось, що про клас питає.

ПідсумокЩо з цього забрати

Коротко про кожну з чотирьох речей, які тема заміряла.

ТвердженняЧим доведено
пляшкове горло змушує вивчити сутьвосьми чисел досить; далі подвоєння дає 11 % замість 30 %
впевненість класифікатора не є мірою якості генераціїбіле полотно дістає 0.9997 проти 0.9913 у справжніх фігур
автоенкодер не є генератором0.0 % його кодів лежать там, звідки ми тягнемо z
дві метрики впорядковують протилежнонепересічні купи в обидва боки на автоенкодері й β=4

І окремо — про метод, бо він у цій темі важливіший за результат. Метрику генерації калібрують перед тим, як їй вірити. Не після того, як числа не зійшлися, а до першого заміру. Вісім завідомо поганих наборів коштують десяти рядків коду й рятують від охайної таблиці з протилежним до правди висновком.

Це та сама лінія, що йде крізь увесь курс. У темі 29 точність по пікселях 0.9001 стояла поруч із mIoU рівно нуль. У темі 32 mIoU дорівнював одиниці при PQ 0.5292. У темі 35 ті самі ознаки давали 0.4000 при усередненні по часу і 1.0000 при розгортці часової осі. Блок 8 доводить її до межі: у генерації метрики, яку можна цитувати саму, не існує взагалі — і саме тому для неї довелося вигадувати окрему метрику FID, що дивиться на розподіл, а не на окремий знімок; наступна тема розбирає її формулу.

Що лишилось незробленим. VAE полагодив місце, звідки беруть z, але заплатив розмиттям: наші зразки при β=0.2 упізнавані, та жоден із них не має різкого краю. Причина проста й неусувна в межах цього підходу — втрата відновлення усереднює. Коли модель не впевнена, де саме межа фігури, найдешевший спосіб зменшити середній квадрат різниці — намалювати межу трохи скрізь.

Наступна тема відмовляється від явної втрати відновлення взагалі. Замість того щоб порівнювати намальоване з оригіналом попіксельно, вона ставить проти генератора суперника — другу мережу, яка вчиться відрізняти підробку від справжнього. Розмиття зникає, бо суперник його одразу помічає. А натомість зʼявляється хвороба, якої в автоенкодерів немає: генератор може знайти дві-три картинки, що надійно обманюють суперника, і почати малювати тільки їх. Ця хвороба зветься колапсом мод, і міряти її ми вже вміємо — покриттям класів, яке ця тема відкалібрувала.