Чернетка

Залишкові мережі: чому глибші мережі навчалися гірше та як обхідне з’єднання це виправило

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

Це дивно, адже глибина має бути вільною ємністю. Більша мережа містить меншу як особливий випадок — тому в гіршому випадку вона повинна мати можливість відповідати меншій мережі та зупинятися на досягнутому. Натомість додавання шарів активно шкодить. Це проблема деградації, і архітектура, яка її вирішила — залишкова мережа, або ResNet (Він та ін., 2015) — тепер є настільки стандартною, що ви користуєтеся нею, не помічаючи: кожен трансформаторний блок, кожна сучасна модель бачення побудована на запровадженому нею однолінійному трюку.

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

Точніше проблема деградації

Ось експеримент, який назвав це. Тренування простої згорткової мережі з 20 шарів на CIFAR-10, а потім простої 56-шарової версії того самого дизайну. 56-шарова мережа має вищу похибку навчання, ніж 20-шарова — і, отже, також більшу похибку тестування. Те ж саме відбувається в ImageNet: 34-шарова звичайна мережа тренується до більшої помилки, ніж 18-шарова звичайна мережа.

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

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

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

Це не обмеження апаратного забезпечення чи бюджету. Більш глибока мережа отримувала той самий (або більше) час навчання, але все одно приземлялася вище. Зійшлося — на гірше.

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

Чому це дивно: аргумент відображення ідентичності

Ось аргумент, який перетворює «глибшу мережу, навчену гірше» з легкого розчарування на справжню головоломку.

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

Ця конструкція існує завжди. Таким чином, існує 56-рівнева мережа, яка принаймні така ж хороша, як 20-шарова мережа. Більш глибоку модель ніколи не можна змусити працювати гірше, ніж дрібнішу — вона завжди може емулювати її, встановивши надлишкові шари на ідентичність і скопіювавши решту.

Це головний момент: оскільки завжди існує рішення, яке зв’язує неглибоку мережу, добре працюючий оптимізатор ніколи не повинен повертати більш глибоку мережу, яка працює гірше. Але це саме те, що сталося. Висновок неминучий — оптимізатор не зміг знайти рішення для збереження ідентичності. Попросити стек нелінійних шарів (кожен — вагова матриця, нормалізація, ReLU) спільно навчитися «виводити саме те, що вам дали», виявляється важко. Ідентичність — це незручна функція, яку можна виразити як продукт навчених нелінійних перетворень, і градієнтний спуск не натрапляє на неї.

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

Виправлення: вивчайте залишок, а не все

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

Візьмемо стек шарів, для яких ми хочемо обчислити деяке цільове відображення H(x). Замість того, щоб просити шари створити H(x) безпосередньо, попросіть їх створити лише різницю від вхідних даних:

F(x)=H(x)−xF(x) = H(x) - x

Потім відновіть ціль, додавши назад вхідні дані:

H(x)=F(x)+xH(x) = F(x) + x

Це + x — це вся ідея. Це коротке з’єднання (також називається пропускаючим з’єднанням): вхідні дані переносяться вперед і додаються до вихідних даних шарів. На схемі:

        x ───────────────┐
        │                │
        ▼                │
   [ weight layer ]      │   (the shortcut: identity,
        │                │    carried straight across)
       ReLU              │
        │                │
   [ weight layer ]      │
        │                ▼
       F(x) ──────────► (+) ──► H(x) = F(x) + x

Шари в середині тепер дізнаються F(x), залишок — що додати до вхідних даних, а не чим його замінити. Звідси «залишкова мережа».

Чому це вирішує проблему деградації? Подивіться, що зараз потрібно для вираження особистості. Щоб блок діяв як ідентифікатор, йому потрібен H(x) = x, що означає F(x) = 0. Довести стопку ваг до нуля легко — це те, що затухання ваги робить безкоштовно, спуск градієнта напрямку змінюється, коли шар не тягне свою вагу. Порівняйте це з простою мережею, де діяти як ідентичність означало, що рівні повинні були активно вивчати трансформацію ідентичності. Ми поміняли важку ціль (вивчення ідентичності) на тривіальну (вивчення нуля). Додаткова глибина тепер може бути нешкідливою за замовчуванням: залишковий блок, який не має нічого спільного, просто штовхає F до нуля і дозволяє x пройти.

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

Додаткова перевага: швидкісне шосе

Є ще одна причина, чому залишки допомагають, і це та, що найчастіше цитується — але її варто поставити на належне місце, після історії деградації, тому що це не було основною мотивацією ResNet (автори чітко заявили, що пакетна нормалізація вже подбала про прості градієнти, що зникають).

Розглянемо зворотний прохід через залишковий блок, y = x + F(x). За правилом ланцюга градієнт втрат відносно входу блоку:

∂L∂x=∂L∂y(1+∂F∂x)\frac{\partial \mathcal{L}}{\partial x} = \frac{\partial \mathcal{L}}{\partial y}\left(1 + \frac{\partial F}{\partial x}\right)

1 є наслідком скорочення. Це означає, що навіть якщо член \partial F / \partial x є крихітним — ситуація, яка спричиняє зникнення градієнтів у звичайному стеку — градієнт все одно повертається через шлях + 1 незменшеним. Швидкий шлях — це чистий канал, який переносить навчальний сигнал прямо від виходу назад до входу, минаючи ослаблення, яке в іншому випадку спричинив би довгий ланцюжок множень. Складіть сто блоків, і градієнт від верху все одно може досягти низу, тому що в кожному блоці є шлях +1, яким він може проїхати.

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

Побачити це: звичайний стек проти залишкового стека

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

import keras
from keras import layers

def plain_block(x, width):
    h = layers.Dense(width, kernel_initializer="he_normal")(x)
    h = layers.BatchNormalization()(h)
    h = layers.Activation("relu")(h)
    h = layers.Dense(width, kernel_initializer="he_normal")(h)
    h = layers.BatchNormalization()(h)
    return layers.Activation("relu")(h)

def residual_block(x, width):
    h = layers.Dense(width, kernel_initializer="he_normal")(x)
    h = layers.BatchNormalization()(h)
    h = layers.Activation("relu")(h)
    h = layers.Dense(width, kernel_initializer="he_normal")(h)
    h = layers.BatchNormalization()(h)
    h = layers.Add()([h, x])              # the shortcut: H(x) = F(x) + x
    return layers.Activation("relu")(h)

def build(depth, width, block):
    inp = keras.Input(shape=(784,))
    x = layers.Dense(width, kernel_initializer="he_normal")(inp)
    for _ in range(depth):
        x = block(x, width)
    out = layers.Dense(10, activation="softmax")(x)
    return keras.Model(inp, out)

Навчіть build(depth=30, width=128, block=plain_block) і build(depth=30, width=128, block=residual_block) на тих самих даних за допомогою того самого оптимізатора, і розрив з’явиться на кривій навчання: звичайна 30-блокова мережа знаходиться на плато при вищих втратах на навчання — це не може знизити помилку так, як це може зробити дрібніша звичайна мережа — тоді як залишкова версія продовжує спадати. Ці дві моделі мають майже ідентичні параметри; єдина різниця – лінія layers.Add(). Це єдине доповнення є різницею між стеком, який погіршується з глибиною, і тим, який отримує від цього користь.

(Зверніть увагу на порядок усередині блоку — Dense → BatchNorm → ReLU, потім додайте — і що ярлик тут є простою ідентифікацією, яка працює, оскільки кожен блок зберігає той самий width. Коли блок змінює розміри, ярлик потребує лінійної проекції на x, щоб відповідати формам перед додаванням; це ярлик проекції в документі ResNet.)

Куди це поділося

Негайним результатом ResNet стала глибина, яка була недосяжною: папір навчив мережі з 50, 101 і 152 шарів на ImageNet — на порядок глибше, ніж те, що працювало раніше — і з ними виграв змагання ImageNet 2015 року. Пізніша робота просунула звичайні залишкові стеки на тисячу шарів, що доцільно спробувати, оскільки ярлик зберігає як прямий ідентифікатор, так і зворотний градієнт добре поводяться на будь-якій глибині.

Але більш важливою спадщиною є те, що ярлик став компонентом за замовчуванням глибоких архітектур, а не спеціальним трюком для бачення. Найбільш послідовним спадкоємцем є трансформатор: кожен блок трансформатора загортає свою увагу та підрівні прямого зв’язку саме за цим шаблоном y = SubLayer(x) + x, що дає змогу складати блок дванадцять, дев’яносто шість або кілька сотень разів. У цьому налаштуванні запущений ланцюжок + x отримує ім’я — залишковий потік — і стає центральним об’єктом, з якого кожна голова уваги та кожен рівень прямого зв’язку читає та записує дельту. Ця історія, а також те, як залишки поєднуються з нормалізацією шару, щоб підтримувати можливість навчання глибокого трансформатора, є темою Всередині блоку трансформатора.

Наскрізну лінію варто вказати прямо. Глибина корисна лише тоді, коли додавання шарів не може погіршити ситуацію — і спосіб гарантувати це — зробити «нічого не робити» простим за замовчуванням, тож оптимізатор досягає справжнього вдосконалення лише тоді, коли він платить. Мережа, яка може дешево передавати свої вхідні дані, — це мережа, яку ви можете собі дозволити зробити дуже глибокою. Це вся ідея, і вона вписується в одну лінію: H(x) = F(x) + x.

Перегляд зображення

Змінюйте масштаб жестом щипка або клавішами + / −. Переміщуйте зображення перетягуванням або стрілками. Esc — закрити.