Перенос навчання

Мережа, навчена кимось іншим на іншій задачі, уже вміє дивитись. Це вміння можна забрати собі — і воно рятує, поки даних мало. А коли даних стане досить, воно почне заважати.

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

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

Наскрізний приклад теми. Дві задачі з одного світу. Джерело — три фігури 28×28: коло, квадрат, ромб; прикладів багато, 1500. Ціль — три інші фігури: кільце, хрест, трикутник; прикладів мало. Класи не перетинаються, тож переносяться не відповіді, а вміння дивитись. Датасет ми не завантажуємо, а малюємо формулами. Усі числа нижче заміряні на цій парі задач.
Чому джерело ми навчаємо самі, а не беремо ImageNet. Реальний перенос починається з рядка resnet18(weights="IMAGENET1K_V1") — і цей рядок тягне з інтернету 44.7 МБ чужих ваг. У цій темі джерельну модель ми навчаємо самі, менш ніж за хвилину, і це не поступка: так видно, звідки взялися «передтреновані» ваги, чого не видно, коли їх завантажують готовими. Як це роблять зі справжніми вагами — у розділі 8.

СитуаціяДаних мало, і більше не буде

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

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

Що робить мережа, навчена з нуля на такій вибірці, ми вже знаємо з теми 11: вона запамʼятовує. Ось наше число — маленька CNN на 24 цільових прикладах, перевірена на 300 нових:

точність із нуля на 24 прикладах = 0.613    при рівні вгадування 0.333

Це не нуль, але й не результат. Мережа щось ухопила — і зупинилась, бо ловити більше нема з чого.

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

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

ІдеяЧуже вміння дивитись

Головна думка теми вміщається в одне речення: більшість того, чого мережа навчається на зображеннях, не залежить від того, що саме вона класифікує.

У темі 07 ми дивилися на ваги першого згорткового шару — вісім табличок 3×3, які мережа вигадала сама: перепади яскравості під різними кутами, плями, куточки. Спитаймо: чи залежить те, що корисно бачити мережі, від того, кільце перед нею чи квадрат?

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

Це найпопулярніший доказ переносу навчання: у будь-якому підручнику є картинка з двома банками фільтрів, і підпис каже «дивіться, вони однакові». Спробуймо перевірити його числом, а не оком.

Перші фільтри трьох різних мереж

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

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

І ось перша неприємність теми. Фільтри цілі схожі на фільтри джерела на 0.616 — але на випадкові, ніким не навчені ваги вони схожі на 0.578. Різниця 0.038 на трьох знаках. Тобто ця перевірка не відрізняє навчену мережу від набору випадкових чисел.

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

Що з цього не випливає. Не випливає, що перенос не працює: він працює, і в розділі 5 ми поміряємо, наскільки. Випливає інше — що «подивись на фільтри, вони схожі» не є доказом. Єдиний чесний доказ користі від чужого тіла — точність на цільовій задачі, і саме її ми й міряємо в цій темі. У розділі 7 ця думка стане ще різкішою: два джерела з майже однаковими фільтрами дадуть точність, що відрізняється більш ніж на третину.
Що ж усе-таки переноситься. Ідея з перепадами яскравості не хибна — просто вона стосується не самих табличок, а того, як мережа складає з них щось більше. Другий шар збирає з перепадів кути й дуги, третій — щось на кшталт «замкнута крива»; і от цей другий рівень уже привʼязаний до того, що саме мережа розрізняла. Наскільки саме привʼязаний — поміряємо в розділі 6.

БудоваТіло й голова

Щоб забрати чуже вміння, мережу треба розрізати. Розріз стандартний і має усталені назви.

ЧастинаЩо робитьУ нашій мережі
Тіло (backbone)перетворює зображення на компактний опис — набір чисел, у якому вже видно прикметитри блоки Conv → ReLU → Pool, 5 888 параметрів
Голова (head)ухвалює рішення: із цього опису робить відповідь потрібного виглядуFlatten і два повнозвʼязні шари, 18 691 параметрів

Наше тіло приймає зображення 1×28×28 і видає 32 канали по 3×3 — тобто 288 чисел. Ці 288 чисел і є представлення (representation): стиснений опис того, що на картинці. Голова далі перетворює 288 чисел на три — по одному на клас.

Тепер зверни увагу на розподіл. У голові 18 691 параметрів проти 5 888 у тілі, тобто 76.0 % усієї мережі. Це не помилка проєктування, а властивість, яку ми порахували ще в темі 09: ваги згорткової мережі живуть у її голові, а робота — у тілі.

Чому числа трохи інші, ніж у темі 09. Тіло тут те саме — ті самі 5 888 ваг. Коротша голова: у темах 09-11 задача була на шість класів фігур, тож остання ланка мала вигляд Linear(64 → 6) — це 390 ваг. Тут ціль — задача на три класи, остання ланка Linear(64 → 3), а це 195 ваг. Різниця рівно 195: голова важить 18 691 замість 18 886, а вся мережа — 24 579 замість 24 774. На частку голови це майже не впливає: 76.0 % тут проти 76.2 % там.

Далі — найпростіша можлива операція, і саме вона зветься переносом навчання:

Крок 1 — узяти чужі ваги

Ваги моделі в PyTorch — це звичайний словник: імʼя шару → таблиця чисел. Він зветься state_dict, і саме його зберігають у файл. Наш важить 100.4 КБ; ваги ResNet-18 з ImageNet важать 44.7 МБ, але влаштовані так само.

source_model.state_dict() → {'body.0.0.weight': [8,1,3,3], … } torch.save(state, 'weights.pt') new_model.load_state_dict(torch.load('weights.pt'))
Крок 2 — викинути голову

Стара голова вміє відповідати «коло / квадрат / ромб». Нам потрібні інші три відповіді, та й кількість класів у цілі може бути будь-яка. Тому голову замінюють цілком на нову, з випадковими вагами:

model.head = nn.Sequential(nn.Flatten(), nn.Linear(288, 64), nn.ReLU(), nn.Linear(64, 3))

Це один рядок — і саме заради нього тіло й голову тримають окремими полями, а не одним довгим Sequential.

Крок 3 — вирішити, що вчиться

Нова голова вчитиметься точно. А от чи вчиться тіло — це вибір, і в нього є три відповіді. Про них — наступний розділ.

Голову не «донавчають» — її створюють заново. Спокуса лишити стару голову й просто продовжити навчання здається економною, але виходить навпаки: перші епохи підуть на те, щоб забути старі відповіді. А якщо кількість класів у цілі інша, стара голова просто не підійде за формою — Linear(64, 3) не видає пʼять чисел.

СпособиТри способи переносу

Голову ми замінили. Тепер вирішуємо долю тіла — і варіантів рівно три.

СпосібЩо робимоЩо вчиться
Ознаки як єтіло беремо чуже й забороняємо йому змінюватисьлише голова
Донавчаннятіло беремо чуже, але дозволяємо змінюватисьуся мережа
Проміжнийспершу вчимо саму голову, потім розморожуємо все й ідемо дрібним крокомспершу голова, потім усе

Перший спосіб — «ознаки як є» (feature extraction). Тіло перетворюється на нерухому функцію: подав зображення — отримав 288 чисел. Мережа перестає бути мережею й стає видобувачем ознак, точно як HOG чи SIFT із теми 05 — з тією різницею, що ці ознаки не придумані людиною, а вивчені з даних. Далі поверх них учиться маленький класифікатор, і вчиться швидко: вагів у нього мало.

Другий спосіб — донавчання (fine-tuning). Тіло теж бере чужі ваги, але їх дозволено рухати. Мережа може пристосувати навіть найглибші прикмети під нову задачу — і, якщо не перестаратись, вийде краще. Ціна: параметрів учиться в багато разів більше, а прикладів у нас двадцять чотири.

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

Що заморожено, а що вчиться

Схема мережі: сірим і з замком — те, що не змінюється, кольоровим — те, що вчиться. Числа зміряні на 24 цільових прикладах. Дивись тут на кількість ваг, а не на порівняння точностей: у донавчання показане налаштування lr=5e-4, найгірше з усієї сітки, і чесне порівняння способів іде нижче окремою таблицею.

спосіб
точність—
навчуваних ваг—
секунд на навчання—
звідки тіло—
Донавчання й навчання з нуля рухають однакову кількість ваг — 24 579. Різниця тільки в тому, звідки ці ваги стартують: із випадкових чисел чи з чужого рішення. Заморожене тіло рухає 18 691 — усього в 1.3 раза менше, бо в цій мережі 76 % ваг і так сидить у голові. У ResNet-18 те саме заморожування прибрало б з навчання 99 % ваг.

Донавчання проти замороженого тіла: обережно з висновком

Тепер найтонше місце теми. Порівняємо обидва способи чесно — на одній і тій самій сітці налаштувань, 24 цільові приклади:

режимlrепохточність
заморожене5e-4200.783
заморожене5e-4400.797
заморожене1e-3200.803
заморожене1e-3400.847
заморожене3e-3200.847
заморожене3e-3400.877
донавчання5e-4200.770
донавчання5e-4400.827
донавчання1e-3200.827
донавчання1e-3400.860
донавчання3e-3200.857
донавчання3e-3400.853
голова, потім тіло2e-4400.850

Тепер найважливіше — і воно не збігається з тим, що пишуть у більшості туторіалів. Часто кажуть: «донавчання дає вищу стелю, просто його важче налаштувати». У нашому досліді вищої стелі в донавчання немає: найкраще заморожене тіло дало 0.877, найкраще донавчання — 0.860. Немає й обіцяної різниці в чутливості: розкид по шести налаштуваннях вийшов 0.094 у замороженого тіла і 0.090 у донавчання, тобто однаковий. Проміжний варіант — спершу голова, потім усе тіло дрібним кроком — дав 0.850 і теж нікого не обігнав.

Не поспішай узагальнювати це на всі задачі: наше тіло має 5 888 ваг проти 18 691 у голові, тобто рухати в ньому майже нічого. У справжній ResNet-18 співвідношення протилежне — у тілі одинадцять мільйонів ваг, і там донавчання справді відкриває інший порядок можливостей. Але саме тому правило «донавчання зазвичай краще» не можна брати на віру: воно виведене на великих мережах і на маленькій не працює.

Чутливість до налаштувань

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

спосіб
точність—
розкид способу—
Дивись на довжину смуги, а не на висоту точки. Обидві смуги вийшли однакової довжини: 0.094 у замороженого тіла і 0.090 у донавчання. Спільне в обох одне: найгірший режим — малий крок і мало епох (5e-4, 20). Недонавчена модель виглядає як невдалий метод, хоч метод тут ні до чого.
Як обирати на практиці. Починай із замороженого тіла: воно дешевше, і на ньому видно, чи взагалі є що переносити. Якщо точності бракує, а даних у тебе тисячі й задача помітно відрізняється від джерельної — переходь до донавчання, і краще у два етапи, з малим кроком для тіла. Але вимірюй обидва варіанти: як показує наш дослід, порядок «донавчання завжди краще» — не закон, а поширене припущення.
Спільна помилка обох способів. Подивись на нижні рядки таблиці: і заморожене тіло, і донавчання показують найгірший результат при lr=5e-4 та 20 епохах — 0.783 і 0.770. Це не властивість методу, а недонавченість: на 24 прикладах із батчем 8 двадцять епох дають рівно 60 кроків градієнтного спуску. Перш ніж робити висновок «цей спосіб гірший», переконайся, що обидва встигли доїхати.

МежаГоловне питання: скільки в тебе даних

Досі ми обирали між способами переносу. Тепер поставимо питання, яке треба ставити першим: а чи взагалі переносити?

Дослід простий. Беремо ту саму цільову задачу й міняємо єдину річ — скільки в нас цільових прикладів: 12, 24, 60, 120 або 240. У кожній точці навчаємо дві моделі: одну з нуля, другу із замороженим чужим тілом. Тест той самий, 300 нових зображень.

прикладів у ціліз нулязаморожене тілорізниця
120.5300.667+0.137
240.6130.847+0.234
600.8300.947+0.117
1200.9630.967+0.004
2400.9930.970−0.023

Дивись на колонку різниці згори вниз: +0.137, +0.234, +0.117, +0.004, −0.023. Виграш переносу спершу росте, потім тане, а на 240 прикладах міняє знак: навчання з нуля дає 0.993 проти 0.970 у переносу. Сам розрив невеликий — сім зображень із трьохсот, — але напрямок стійкий: починаючи зі 120 прикладів виграшу від чужого тіла вже немає. Це і є розворот, заради якого написана вся тема.

Розворот

Дві криві на одній парі осей. Повзунок обирає розмір цільової вибірки; вертикальна лінія показує, де ти зараз.

з нуля—
перенос—
виграш переносу—
Криві перетинаються. Ліворуч від перетину чуже тіло — єдине джерело інформації, якого в задачі більше нема; праворуч воно вже не додає нічого, зате тримає модель у формі, підігнаній під чужі класи. Рівень випадкового вгадування на трьох класах — 0.333.

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

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

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

ГлибинаЩо переноситься, а що ні

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

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

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

Скільки блоків заморозити

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

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

А тепер чесний висновок, і він не той, на який ми розраховували. Різниці майже немає.

замороженовільних ваг24 приклади120 прикладів
нічого24 5790.8570.957
перший блок24 4990.8470.953
перші два23 3310.8400.967
усе тіло18 6910.8470.967

На 24 прикладах усі чотири варіанти вклалися в смугу завширшки 0.017 — це пʼять зображень із трьохсот, тобто шум. На 120 прикладах різниця теж мізерна, і вона протилежна очікуванню: трохи попереду не «розморозити пізні шари», а заморозити все (0.967 проти 0.957).

Чому так — видно, якщо подивитись на колонку вільних ваг. Між «нічого не заморожено» і «заморожено все» різниця всього 24 579 проти 18 691, тобто менш ніж чверть мережі: у нашій мережі 76 % ваг сидить у голові, і голова вчиться завжди. Ми просто не маємо чого заморожувати.

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

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

ШкодаКоли перенос шкодить

Усі попередні заміри мали спільну обставину, яку легко не помітити: джерело було схоже на ціль. Ті самі 28×28, та сама біла фігура на чорному тлі, той самий шум, той самий масштаб. Змінювались лише класи.

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

Схоже джерело проти далекого

Ліворуч — як виглядають зображення джерельного набору. Праворуч — та сама пара кривих, що й у розділі 5, але тіло взяте з обраного джерела.

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

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

прикладівз нуляблизьке джерелодалеке джерело
120.5300.6670.357
240.6130.8470.543
600.8300.9470.500
1200.9630.9670.570
2400.9930.9700.670

На 12 прикладах далеке джерело дає 0.357 — це майже рівень випадкового вгадування (0.333) і на 0.173 нижче за навчання з нуля. Далі воно росте, але надто повільно: на 240 прикладах усе ще 0.670 проти 0.993 у мережі, навченої з нуля. Розрив не звужується, а розходиться.

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

Порівняй із тим, що каже перший інтерактив. Фільтри цих двох джерел на око однакові, і числом теж: 0.616 у близького проти 0.627 у далекого. А точність відрізняється в кожній точці таблиці вище — від 0.300 на 240 прикладах до 0.447 на шістдесяти. Ось найкоротший доказ, що знання мережі живе не в першому шарі: перші фільтри в усіх однакові, а користь від тіла — зовсім різна.

Де це трапляється насправді

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

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

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

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

ПрактикаЯк це роблять насправді

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

Звідки беруть моделі. Два основні джерела:

Як заморожують. У PyTorch заморожування — це два кроки, і другий забувають частіше, ніж перший:

for parameter in model.body.parameters(): parameter.requires_grad = False optimizer = torch.optim.Adam( [p for p in model.parameters() if p.requires_grad], lr=3e-3)

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

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

optimizer = torch.optim.Adam([ {'params': model.body.parameters(), 'lr': 1e-4}, # чуже — рухаємо обережно {'params': model.head.parameters(), 'lr': 1e-3}, # своє — рухаємо вільно ])

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

Нормалізація мусить бути та сама

А тепер найчастіша помилка переносу, і вона не про архітектуру.

Тіло навчалося на числах, які хтось привів до певного вигляду. Для моделей ImageNet цей вигляд відомий і записаний у документації: кожен канал зменшують на своє середнє й ділять на своє стандартне відхилення, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]. Ці числа не магічні — це середнє й розкид яскравості по мільйону фотографій ImageNet.

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

Не та нормалізація

Та сама передтренована модель із замороженим тілом, але цільові дані приведено до різного вигляду. Голова щоразу вчиться заново — тобто в неї є всі шанси пристосуватись.

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

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

weights = ResNet18_Weights.IMAGENET1K_V1 model = resnet18(weights=weights) preprocess = weights.transforms() # саме та нормалізація й той розмір

РозмірРозмір входу

Друга умова, яку тіло приносить із собою, — розмір зображення. Майже всі передтреновані мережі очікують 224×224, і це число теж не магічне: воно закріпилось із ImageNet і зручно ділиться навпіл пʼять разів (224 → 112 → 56 → 28 → 14 → 7).

Наскільки жорстка ця вимога, залежить від голови — і ми це вже розбирали в темі 09. Якщо голова починається з Flatten, розмір зашитий намертво: кількість чисел після розгортання залежить від сторони зображення, а Linear вимагає рівно стільки входів, скільки в нього ваг. Якщо голова починається з глобального усереднення (GAP), вона бачить лише кількість каналів — і приймає будь-який розмір, який тіло здатне пропустити.

ГоловаІнший розмір входуЧому
Flatten → Linearпомилка розмірукількість чисел після Flatten залежить від сторони зображення
GAP → Linearпрацюєусереднення прибирає просторовий розмір, лишається кількість каналів

Flatten прибитий до сторони, усереднення — ні

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

після Flatten—
після усереднення—
чого чекає голова з Flatten—
Одне число з каналу — і сторона перестає важити. Flatten віддає голові 32 × сторона × сторона чисел, тож голова, зібрана під 288, працює рівно на одній стороні карти й на жодній іншій. Глобальне усереднення стискає кожен канал до одного числа: голова бачить 32 входи за будь-якої сторони. Саме тому передтреновану мережу з такою головою можна годувати входом іншого розміру, а з Flatten — ні.

Але «працює» не означає «працює добре». Тіло вчилося на певному масштабі: предмет на 224×224 займає десь сотню пікселів, і під це підігнані і рецептивне поле, і розміри прикмет. Подаси 64×64 — предмет стане замалим для тих фільтрів, які тіло вміє; подаси 1024×1024 — фільтри бачитимуть фактуру, а не форму.

Як приводити до потрібного розміру. Тут повертається термін із теми 03. Розтягнути зображення до квадрата просто, але геометрія псується: коло стає овалом, а тіло вчилося на неспотворених формах. Обрізати — втратиш частину кадру. letterbox зменшує за меншим із двох коефіцієнтів і доповнює решту однотонними полями: пропорції цілі, втрачається лише частина площі під порожнечу.

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

ПравоУ ваг є ліцензія

Останнє, про що варто знати до того, як воно стане проблемою. Ваги — це не код і не дані; це окремий обʼєкт, і в нього своя ліцензія.

Ліцензія бібліотеки й ліцензія ваг — різні речі. PyTorch і torchvision під BSD, timm під Apache 2.0 — з кодом усе просто. А ваги в timm зібрані з десятків репозиторіїв, і в кожного своя умова: десь Apache, десь MIT, десь CC BY-NC, тобто без комерційного використання, а десь окрема угода, яку треба прийняти на сторінці моделі.

Що з цього випливає практично:

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

ПідсумокБлок 2 закрито

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

ТемаЩо вона дала
06 · Чому не повнозвʼязнаТри ліки від однієї хвороби: локальність, спільні ваги, багато карт. MLP на зсунутих предметах падає до рівня вгадування, маючи в 13 разів більше параметрів.
07 · Згортковий шарЯдро, канали, stride, padding. І перший погляд на вивчені фільтри — той самий, на який ми сьогодні спиралися.
08 · ПулінгЗменшення карти й часткова терпимість до зсуву. Часткова — це слово ми теж перевіряли числом.
09 · CNN цілкомСкладання: блок, стос блоків, наскрізний рахунок форм. Тут же — розділення на тіло й голову, без якого сьогоднішня тема не існувала б.
10 · АугментаціяЯк із одного зображення зробити багато й де межа корисних перетворень.
11 · Навчання CNNРежим навчання: батчнорм, розклад швидкості, рання зупинка, боротьба з перенавчанням.
12 · Перенос навчанняЧуже знання: коли воно рятує, коли заважає й коли шкодить.

Через усі сім тем проходить одна думка, і вона не про згортки. Кожен прийом у цьому блоці — це обмеження, накладене на мережу свідомо. Локальність забороняє нейрону дивитися на все зображення. Спільні ваги забороняють мати різні детектори для різних кутів кадру. Пулінг забороняє розрізняти сусідні положення. Аугментація забороняє покладатися на орієнтацію предмета. Заморожене тіло забороняє міняти представлення. Кожна заборона зменшує простір, у якому мережа шукає рішення, — і саме тому вона знаходить його на малих даних.

А з цього випливає й межа: обмеження, яке рятує на малих даних, стає стелею на великих. Ми бачили це двічі. У темі 08 пулінг дав терпимість до зсуву й разом із нею — втрату точного положення. Сьогодні заморожене тіло дало виграш +0.137 на 12 прикладах і програш −0.023 на 240. Це один і той самий закон, і, зустрівши його втретє, ти вже впізнаєш його з першого погляду.

Що далі

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

Наступний блок починається з AlexNet і VGG. Це не історичний екскурс: у 2012 році на змаганні ImageNet згорткова мережа виграла в класичних ознак із теми 05 з таким відривом, що галузь розвернулася за один рік. Далі — ResNet і скіп-зʼєднання, де глибина впирається в стіну й перестрибує її одним зʼєднанням.

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