Дванадцять тем поспіль ми ставили мережі одне питання: наскільки точно вона відповідає. AlexNet і VGG відповіли на нього глибиною й кількістю фільтрів, ResNet — скіп-зʼєднанням, яке зробило глибину керованою. А потім мережі довелося вийти з серверної: розблокувати телефон обличчям, знайти документ у кадрі камери, розмити тло під час відеодзвінка просто в браузері. І виявилось, що питання «наскільки точно» — не єдине, а іноді й не головне.
Ця тема про три архітектурні родини, які виникли з другого питання: Inception (2014), MobileNet (2017) і EfficientNet (2019). Але головне в ній не вони, а те, що зʼясувалося дорогою: «дешевше» — не одне число, а три, і вони посварені між собою.
ОбмеженняМережа має кудись поміститись
Коли модель живе на сервері, її обмеження — гроші й нічого більше. Мало памʼяті — докупи. Повільно — постав другу машину. На пристрої в кишені все інакше, і обмежень стає щонайменше чотири.
| Обмеження | Звідки береться | Що станеться, якщо порушити |
|---|---|---|
| Розмір завантаження | Користувач бачить вагу застосунку в магазині | Не встановить. Ваги VGG16 — 553 МБ |
| Оперативна памʼять | Телефон дає застосунку сотні мегабайтів на все: і ваги, і проміжні карти ознак | Система вбʼє застосунок без пояснень |
| Затримка | Камера дає 30 кадрів на секунду — 33 мс на кадр | Картинка смикається. Це дратує сильніше за кілька відсотків точності |
| Батарея й нагрів | Кожне множення — це заряд, а гарячий телефон сам знижує частоту | Мережа вклалась у бюджет на холодному телефоні й вилетіла з нього через хвилину |
Останній рядок найпідступніший: його не видно на жодному замірі, зробленому на робочому столі. А браузер додає своє — там немає ані відеокарти в звичному сенсі, ані права качати сотні мегабайтів перед першим кадром.
Три числаТри різні «дешевше»
Спершу треба розібратися, що ми взагалі міряємо: слово «дешевше» ховає три різні величини, і плутати їх коштує дорого.
Скільки чисел треба зберігати. Це вага файлу з моделлю: кожне число в
звичайному форматі float32 займає 4 байти. Мільйон параметрів — 4 МБ.
Це те, що обмежує завантаження й памʼять. І це єдине з трьох чисел, яке не залежить ані від розміру вхідного зображення, ані від заліза.
Скільки множень мережа робить, щоб опрацювати одне зображення. Для згорткового шару це число рахується точно, і рахувати його ми вміємо ще з теми 07:
Словами: кожен вихідний піксель кожного вихідного каналу — це сума добутків
по вікну k×k у всіх вхідних каналах. Тут cin
і cout — канали на вході й на виході,
H і W — розміри вихідної карти, k — сторона
ядра. Це число залежить від розміру входу, росте разом із ним квадратично
й приблизно відповідає за витрату батареї.
Скільки мілісекунд мине від подачі зображення до відповіді. Єдине, що відчуває користувач, — і єдине, що не рахується формулою: його можна лише заміряти на тому залізі, де мережа працюватиме.
Легко припустити, що всі три впорядковують архітектури однаково: менше параметрів — менше множень — менше часу. Це припущення хибне. Ось девʼять справжніх архітектур, зібраних без ваг (нам потрібна форма, а не вміння), один потік процесора, без відеокарти; час — найкращий із тридцяти прогонів.
| Архітектура | параметрів | ваги, МБ | множень, млн | час, мс |
|---|---|---|---|---|
| AlexNet | 61 100 840 | 244 | 714 | 37.9 |
| VGG16 | 138 357 544 | 553 | 15 470 | 448.0 |
| GoogLeNet | 13 004 888 | 52 | 1 498 | 89.0 |
| ResNet18 | 11 689 512 | 47 | 1 814 | 69.0 |
| ResNet50 | 25 557 032 | 102 | 4 089 | 159.3 |
| Inception-v3 | 27 161 264 | 109 | 5 713 | 235.5 |
| EfficientNet-B0 | 5 288 548 | 21 | 386 | 42.5 |
| MobileNet-v2 | 3 504 872 | 14 | 301 | 32.4 |
| MobileNet-v3-small | 2 542 856 | 10 | 56 | 11.0 |
Від VGG16 до MobileNet-v3-small — 54 рази за параметрами. Але подивись на AlexNet. За параметрами він другий із девʼяти: 61 мільйон, більше за ResNet50 і Inception-v3 разом узяті. За часом він — сьомий, тобто третій із кінця. Ті самі девʼять мереж, два різні порядки.
Три «дешевше» не збігаються
Кожен рядок — архітектура, кожна колонка — своя міра вартості. Кнопки впорядковують рядки за обраною мірою. Цифра в кружечку — місце в цій колонці.
AlexNet: багато ваг, мало роботи. Повнозвʼязний шар на 4096 нейронів має 4096 × 9216 ≈ 37 мільйонів ваг — і робить рівно 37 мільйонів множень. Одна вага — одне множення, і все. Згортковий шар поводиться протилежно: ядро 3×3 на 256 каналів має 590 тисяч ваг, але прикладається до кожного з 28 × 28 положень, тобто робить 462 мільйони множень. Одна вага — сотні множень. Тому мережа з головою на мільйони параметрів важить багато й рахує мало, а мережа з тілом на ті самі мільйони важить мало й рахує довго. Це той самий переїзд ваг з голови в тіло, який тема 14 показала на ResNet, — тільки тепер ми дивимось на нього з боку вартості.
EfficientNet-B0 проти AlexNet: менше всього — а часу стільки ж. У EfficientNet-B0 в 11.6 раза менше параметрів і в 1.85 раза менше множень, ніж в AlexNet. За часом вони практично рівні: 42.5 мс проти 37.9. Тобто зменшили обидва числа, які вміємо рахувати, — і не виграли нічого в тому єдиному, що відчуває користувач.
Це не помилка заміру, а головна незручна правда теми. Повернемось до неї в розділі 06.
InceptionНе обирати розмір ядра
Повернімось у 2014 рік. Проєктувальник мережі мусить для кожного шару відповісти на питання, на яке нема відповіді: яке брати ядро — 1×1, 3×3 чи 5×5?
Питання не дозвільне. Ядро 3×3 бачить дрібну деталь: край, кут, штрих. Ядро 5×5 — більший фрагмент: вигин, кільце, фактуру. А предмети бувають різного розміру навіть в одному кадрі: кіт на передньому плані й кіт удалині займають різну кількість пікселів, і те, що для першого «дрібна деталь», для другого вже цілий кіт.
Класична відповідь — підібрати: побудувати десяток мереж, навчити всі, взяти найкращу. Дорого, а головне — відповідь буде єдиною на всю мережу й на всі зображення.
Автори GoogLeNet відповіли інакше: не обирати. Пропустити вхід через усі варіанти одразу й скласти результати поруч.
вхід: 192 канали, карта 28×28 │ ├── 1×1 ────────────────→ 64 канали ├── 3×3, padding 1 ─────→ 128 каналів ├── 5×5, padding 2 ─────→ 32 канали └── maxpool 3×3 → 1×1 ──→ 32 канали │ torch.cat по каналах ↓ вихід: 256 каналів, карта 28×28
Це і є модуль Inception (Inception module). Чотири гілки рахуються
незалежно від одного й того самого входу, а потім їхні виходи склеюються по
каналах — операцією torch.cat(..., dim=1).
«Склеюються по каналах» варто розкрити. Кожна гілка дає карту тієї самої висоти
й ширини — 28×28, це забезпечено підбором padding; різняться вони лише
кількістю каналів. Склеювання ставить ці стоси один на одного:
64 + 128 + 32 + 32 = 256 каналів тієї самої карти. Нічого не додається й не
усереднюється — результати гілок лежать поруч, і наступний шар сам вирішить, на які
з них дивитись.
Гілка з пулінгом стоїть там із тієї самої логіки: пулінг із теми 08 — теж спосіб подивитись на околицю, тож нехай і він буде одним із варіантів.
ГорлоПляшкове горло 1×1
У щойно намальованому модулі є проблема, і вона арифметична. Порахуймо гілку 5×5 чесно, за формулою параметрів згорткового шару з теми 07:
Підставляємо: вхід 192 канали, вихід 32 канали, ядро 5×5.
Сто пʼятдесят три тисячі параметрів на одну гілку, яка дає всього 32 канали з 256. Гілка 3×3 із її 128 каналами коштує ще більше: 192 × 128 × 9 + 128 = 221 312. Разом модуль важить 393 472 параметри. А таких модулів у GoogLeNet девʼять, і кожен наступний працює з більшою кількістю каналів.
Джерело витрати видно з формули: перший множник — cin,
тобто 192. Дороге не саме ядро 5×5, а те, що воно мусить дивитись на всі 192 вхідні
канали одночасно.
Рішення — те, що ми вже вміємо. Згортка 1×1 з теми 07 не дивиться на сусідів у просторі взагалі: вона бере один піксель і змішує його канали. Тому вона дешева. Поставимо її перед дорогим ядром — щоб стиснути 192 канали до 16, а вже потім прикласти 5×5.
Згортка 1×1 з 16 фільтрами. Кожен фільтр — це 192 числа: скільки взяти від
кожного вхідного каналу. Ядро 1×1 означає «дивись лише на цей піксель», тож
множник k×k дорівнює одиниці й зникає.
Те саме ядро 5×5, той самий вихід у 32 канали. Але вхід тепер не 192 канали, а 16, і перший множник у формулі впав у дванадцять разів.
Гілка подешевшала в 9.6 раза. Обидві частини разом коштують майже вдесятеро менше, ніж прямий шлях.
Ця стисла проміжна ділянка й зветься пляшковим горлом (bottleneck): широкий вхід, вузьке місце посередині, широкий вихід. Те саме зробимо з гілкою 3×3 — стиснемо 192 канали до 96, — і порахуємо модуль повністю.
| Гілка | без горла | з горлом 1×1 | різниця |
|---|---|---|---|
| 1×1 → 64 | 12 352 | 12 352 | — |
| 3×3 → 128 | 221 312 | 129 248 | 1.7× |
| 5×5 → 32 | 153 632 | 15 920 | 9.6× |
| pool → 32 | 6 176 | 6 176 | — |
| модуль цілком | 393 472 | 163 696 | 2.4× |
Гілка 1×1 не міняється — вона вже й є згорткою 1×1, горло їй ні до чого. Гілка з пулінгом теж: пулінг не має ваг, а 1×1 після нього і є її єдиним шаром.
Модуль Inception і його горло
Перемикач прибирає й повертає стискання 1×1. Повзунок міняє кількість вхідних каналів — саме від неї залежить, чи потрібне горло взагалі.
Тепер зрозуміло, чому модуль Inception узагалі можна собі дозволити. Горло — не прикраса до ідеї паралельних гілок, а умова її існування.
torchvision, вийде 13 004 888 параметрів. Але майже половина з них —
допоміжні класифікатори: дві додаткові голови, приліплені до середини мережі,
щоб градієнт доходив до перших шарів. Це той самий біль, який
тема 14 розвʼязала скіп-зʼєднанням, тільки
розвʼязаний милицею. Працюють вони лише під час навчання, а в готовій моделі
просто лежать без діла: зібрана без них — googlenet(aux_logits=False) —
та сама мережа має 6 624 904 параметри. Коли цитуєш розмір моделі, дивись, який саме
з двох чисел ти цитуєш.РозділитиРоздільна згортка по глибині
Горло 1×1 — напівзахід: воно зменшує кількість каналів перед дорогим ядром, але саме ядро й далі робить дві справи одночасно. MobileNet запитав: а навіщо взагалі дві справи в одній операції?
Придивімось до звичайної згортки 3×3 із 64 каналів у 128. Кожен із 128 вихідних каналів — це сума по всіх 64 вхідних каналах, і в кожному з них ще й сума по вікну 3×3. Тобто одна операція відповідає на два різні питання:
- «де» — як пікселі поєднуються зі своїми сусідами в просторі (це робота вікна 3×3);
- «що» — як поєднуються між собою канали, тобто різні виявлені ознаки (це робота підсумовування по 64 каналах).
Ідея MobileNet: розділити ці два питання на дві окремі операції. Спершу тільки «де», окремо в кожному каналі. Потім тільки «що», без огляду на сусідів. Результат зветься роздільною згорткою по глибині (depthwise separable convolution).
Згортка по глибині (depthwise convolution). Кожен вхідний канал отримує своє власне ядро 3×3 і опрацьовується окремо від решти. Канали між собою не перемішуються взагалі: було 64 канали — стало 64 канали.
У PyTorch це не окремий шар, а звичайний nn.Conv2d з аргументом
groups: nn.Conv2d(64, 64, 3, padding=1, groups=64).
Аргумент groups ділить канали на групи, які не бачать одна одну;
коли груп рівно стільки, скільки каналів, кожен канал лишається наодинці.
Точкова згортка (pointwise convolution) — це вже знайома нам згортка 1×1. Вона бере кожен піксель окремо й змішує його 64 канали у 128 нових. Просторових сусідів вона не бачить зовсім — цю роботу вже зробив крок 1.
Той самий вхід, той самий вихід, те саме вікно 3×3 — і в 8.24 раза менше параметрів.
Ми нічого не відкинули: обидві операції разом і далі дивляться на вікно 3×3 в усіх
каналах. Змінилось інше — замість 64 × 128 = 8192 окремих ядер 3×3
(по одному на кожну пару «вхідний канал → вихідний канал») тепер є 64 ядра
3×3 плюс таблиця змішування 64 × 128. Добуток розпався на суму.
Ось як економія росте з кількістю каналів:
| вхід → вихід | звичайна | роздільна | різниця |
|---|---|---|---|
| 32 → 64 | 18 496 | 2 432 | 7.61× |
| 64 → 128 | 73 856 | 8 960 | 8.24× |
| 128 → 256 | 295 168 | 34 304 | 8.60× |
| 256 → 512 | 1 180 160 | 134 144 | 8.80× |
Числа наближаються до девʼяти й не переходять межу. Це не збіг — межу видно з формул. Якщо на секунду відкинути зсуви (їх мало й вони нічого не вирішують), відношення дорівнює:
Прочитаймо це словами. Скоротилось cin —
кількість вхідних каналів на відношення не впливає взагалі. Лишились дві
величини: розмір ядра й кількість вихідних каналів. Коли вихідних каналів
багато, доданок 1/cout стає мізерним, і
відношення прямує до k² = 9. Для ядра 5×5 межа була б 25.
Перевіримо на числах: при k=3 і cout=128
маємо 1 ÷ (0.1111 + 0.0078) = 8.41; у таблиці стоїть 8.24, і різниця — це саме ті
зсуви, які ми відкинули.
Звичайна проти роздільної
Дві схеми однієї операції. Покрути повзунки й подивись, який із них насправді керує економією, а який ні.
c_in від краю до краю — відношення майже не зрушить. Зсунь
c_out — зрушить одразу. А постав ядро 1×1: роздільна стає навіть
трохи дорожчою за звичайну, бо згортка по глибині з вікном в один піксель
уже нічого не робить, а ваги на неї все одно витрачено.РозривЧому економія в 9 разів не дає прискорення в 9 разів
Ми щойно скоротили параметри у 8.24 раза; множення скорочуються приблизно так само — формула та сама, лише помножена на розмір карти. Здається, що й час має впасти у вісім разів. Заміряймо.
Вхід 64 канали, карта 56×56, один шар, один потік процесора:
| операція 64→128 | параметрів | час, мс |
|---|---|---|
| звичайна 3×3 | 73 856 | 6.05 |
| роздільна | 8 960 | 1.36 |
| різниця | 8.24× | 4.45× |
Виграш є, але вдвічі менший за обіцяний — і це ще добрий випадок. На нашій маленькій мережі з фігурами 28×28 роздільна згортка скоротила параметри в 6.2 раза, а час не скоротила майже ніяк:
| мережа на фігурах | параметрів | множень | прохід, мс | точність |
|---|---|---|---|---|
| звичайні згортки | 23 798 | 1 919 616 | 13.02 | 0.973 |
| роздільні | 3 846 | 356 320 | 12.03 | 0.972 |
| різниця | 6.19× | 5.39× | 1.08× | −0.001 |
Обіцяно проти отриманого
Ті самі два заміри, поставлені поруч. Ліворуч від кожної пари — у скільки разів менше стало того, що ми рахуємо; праворуч — у скільки разів менше стало того, що користувач відчуває.
Параметрів у шість разів менше, множень у пʼять разів менше, точність та сама — а часу заощаджено вісім відсотків, і це менше за розкид самого заміру. Чому?
Причина в тому, що процесор не витрачає час на множення. Він витрачає його на очікування чисел із памʼяті.
Процесорне ядро робить кілька множень за такт — за один удар внутрішнього годинника, а таких ударів у сучасного процесора кілька мільярдів на секунду. Дістати ж число з оперативної памʼяті коштує сотні тактів. Тому операція, у якій на кожне прочитане число припадає багато множень, іде швидко — а та, де множення на число одне, повільно, хоч би скільки їх було мало.
Звичайна згортка 64→128 читає вхідну карту й на кожен її піксель робить
128 × 9 = 1152 множення. Згортка по глибині читає рівно ту саму карту,
а робить на піксель 9 множень. Читання те саме, роботи в 128 разів менше —
і час впирається не в роботу, а в читання.
Звичайна згортка всередині бібліотеки перетворюється на множення великих
матриць — операцію, під яку процесорні інструкції та кеші проєктувались
десятиліттями. Згортка по глибині розпадається на cin
крихітних незалежних задач, кожна з яких завузька, щоб заповнити
векторний регістр — комірку процесора, куди влазить одразу вісім або
шістнадцять чисел і яку він множить за одну дію. Наполовину порожній регістр
коштує рівно стільки ж, скільки повний, тож заліза використовується менше,
ніж є.
Це не здогад — це видно в замірах, якщо поділити множення на час. Величина «скільки мільйонів множень залізо встигає за мілісекунду» показує, наскільки повно використано процесор.
| Архітектура | множень, млн | час, мс | млн множень / мс |
|---|---|---|---|
| VGG16 | 15 470 | 448.0 | 34.5 |
| ResNet18 | 1 814 | 69.0 | 26.3 |
| ResNet50 | 4 089 | 159.3 | 25.7 |
| Inception-v3 | 5 713 | 235.5 | 24.3 |
| AlexNet | 714 | 37.9 | 18.8 |
| GoogLeNet | 1 498 | 89.0 | 16.8 |
| MobileNet-v2 | 301 | 32.4 | 9.3 |
| EfficientNet-B0 | 386 | 42.5 | 9.1 |
| MobileNet-v3-small | 56 | 11.0 | 5.1 |
Три підсвічені рядки — це якраз мережі, побудовані на роздільних згортках. Вони використовують процесор у кілька разів гірше за щільні мережі: у наших прогонах розрив між найшвидшою й найповільнішою за цією міркою архітектурою складав від чотирьох до семи разів. VGG16 — груба, стара, безглуздо велика мережа з простих щільних згорток, і саме тому залізу з нею легко: кожне її множення дешеве, їх просто дуже багато.
FLOPs — floating point
operations, операції з дробовими числами, або MACs — multiply-accumulate,
«помножити й додати»; на одну MAC припадає дві FLOP) — не міра швидкості.
Це міра роботи. Дві мережі з однаковою кількістю множень легко відрізняються часом
удвічі. Тому будь-яка заява «наша модель у 5 разів ефективніша» без слова «заміряно
на такому-то залізі» нічого не означає. Міряй час, а не FLOPs.І це пояснює, чому AlexNet у нашій першій таблиці такий швидкий, попри 61 мільйон параметрів. Його вага сидить у повнозвʼязних шарах, а повнозвʼязний шар — це множення матриці на вектор, найкраще відлагоджена операція в усій обчислювальній техніці.
Останній доказ того самого — на роздільності входу. Візьмемо одну й ту саму нашу мережу й подамо їй різні за розміром зображення. Параметри не зміняться зовсім: завдяки глобальному усередненню голова мережі не знає, якого розміру була карта. Множення виростуть квадратично. А час?
Вхід росте — що росте разом із ним
Одна й та сама мережа на 23 798 параметрів, вісім розмірів входу. Повзунок обирає розмір; усе показано відносно 28×28.
РучкиДві ручки MobileNet
Досі ми міняли саму операцію. Але автори MobileNet зробили ще одну річ, зовсім просту й щонайменше таку ж корисну.
Вони визнали, що не знають, який бюджет буде в користувача. Тому замість однієї моделі випустили сімейство й дали дві ручки, якими користувач сам рухається по кривій «точність проти вартості».
Ручка перша: множник ширини
Множник ширини (width multiplier), позначається α — це просто число, на яке множиться кількість каналів у кожному шарі. Було 16, 32, 64 канали — при α = 0.5 стане 8, 16, 32.
Параметри згорткового шару — це
cin × cout × k², і множник
зменшує обидва множники одразу. Отже:
Половина ширини — чверть параметрів, а не половина. Це щедра ручка: невелике зменшення каналів дає велике зменшення вартості. Заміряємо на нашій мережі з фігурами:
| множник | канали | параметрів | множень | прохід, мс | точність |
|---|---|---|---|---|---|
| 0.25 | 4 / 8 / 16 | 1 634 | 141 216 | 3.74 | 0.853 |
| 0.50 | 8 / 16 / 32 | 6 142 | 508 224 | 6.60 | 0.962 |
| 0.75 | 12 / 24 / 48 | 13 530 | 1 101 024 | 10.09 | 0.959 |
| 1.00 | 16 / 32 / 64 | 23 798 | 1 919 616 | 13.02 | 0.973 |
Від 1.00 до 0.50 параметрів стало вчетверо менше, а точність упала на 1.1 пункту: з 0.973 до 0.962. Виглядає як безкоштовна економія — але наскільки стійкі ці числа?
Ми навчили ту саму мережу при α = 0.5 ще двічі, змінивши лише випадкове зерно: 0.962, 0.935, 0.978. Розкид — 4.3 пункту, тобто вчетверо більший за різницю між α = 0.5 і α = 1.0.
Це типова ситуація, а не вада заміру: фігури — легка задача, і мережа з 6 тисячами параметрів розвʼязує її не гірше за мережу з 24 тисячами. Саме тому ручка й потрібна: оптимальна ширина залежить від задачі, а автор архітектури твоєї задачі не знає.
Множник ширини
Чотири заміряні положення. Смуга похибки на точності — це розкид між трьома зернами при α = 0.5.
Ручка друга: множник роздільності
Множник роздільності (resolution multiplier), позначається ρ — це просто менше вхідне зображення. Замість 224×224 подати 192×192, або 160×160, або 128×128.
Діє вона зовсім інакше за першу:
| множник ширини α | множник роздільності ρ | |
|---|---|---|
| Параметри | падають як α² | не змінюються зовсім |
| Множення | падають як α² | падають як ρ² |
| Вага файлу | падає | не змінюється |
| Що втрачаємо | здатність розрізняти багато різних ознак | дрібні деталі, які просто зникли з пікселів |
Отже ручки не взаємозамінні: вагу завантаження зменшує тільки α, а час і батарею — обидві. На нашій задачі та сама мережа на 23 798 параметрів при вході 20×20 замість 28×28 робить 979 584 множення замість 1 919 616 (удвічі менше), витрачає 7.13 мс замість 13.02, а точність дає 0.957 проти 0.973. Параметрів, як і обіцяно, рівно стільки ж.
РазомEfficientNet: масштабувати треба все разом
До 2019 року в інженера було три способи зробити мережу більшою, і кожен використовували поодинці:
- глибина d — більше шарів. Так росла VGG (11 → 16 → 19) і ResNet (18 → 34 → 50 → 101 → 152);
- ширина w — більше каналів у шарі. Так росла Wide ResNet і так само працює ручка α з попереднього розділу;
- роздільність r — більше вхідне зображення.
Автори EfficientNet поставили питання інакше: якщо в тебе вдвічі більший бюджет, куди його вкласти? І показали замірами, що кожен окремий шлях швидко вичерпується. Глибока, але вузька мережа не має чим представляти складні ознаки. Широка, але мілка — не встигає зібрати з простих ознак складні. Велика роздільність без глибини й ширини дає купу пікселів, які нема кому опрацювати.
Звідси складене масштабування (compound scaling): рости треба за всіма трьома осями одночасно й у сталій пропорції. Пропорція задається одним числом — коефіцієнтом φ, який каже, у скільки разів більший бюджет ти готовий витратити:
Тут α, β, γ — три сталі, свої для родини архітектур, а φ — єдина ручка, яку крутить користувач. Запис αφ означає «α, помножене саме на себе φ разів». Самі сталі не довільні — вони повʼязані бюджетом, і ось як кожна вісь впливає на кількість множень:
Удвічі більше шарів — удвічі більше роботи: вартість × d.
У формулі множень канали стоять двічі: cin
і cout. Подвоїти ширину — це вчетверо більше
роботи: вартість × w².
Подвоїти сторону — це вчетверо більше пікселів, і кожен опрацьовується окремо: вартість × r².
Тому й обирають сталі так, щоб α × β² × γ² ≈ 2: тоді кожен крок φ на одиницю подвоює вартість, і бюджет рахується в голові.
Значення, які автори EfficientNet опублікували: α = 1.2, β = 1.1, γ = 1.15. Перевіримо множення: 1.2 × 1.1² × 1.15² = 1.2 × 1.21 × 1.3225 = 1.92 — справді близько до двійки.
Один бюджет, три осі
Три повзунки й спільний бюджет обчислень. Кнопка ставить пропорцію EfficientNet при поточному бюджеті.
ПошукАрхітектуру можна не вигадувати, а шукати
Складене масштабування каже, як збільшувати мережу. Але від чого відштовхуватись? У EfficientNet є базова модель B0, з якої розгортаються B1…B7. Звідки взялась сама B0?
Її не вигадала людина. Її знайшла програма — методом, що зветься пошук архітектур (neural architecture search, NAS): описати простір можливих мереж (скільки блоків, які ядра, де роздільні згортки), а далі перебирати варіанти, навчати кожен і залишати кращі.
Перше, чого не варто недооцінювати: це дорого. Не «треба потужний компʼютер», а сотні й тисячі відеокарто-днів на один пошук. Беручи готову EfficientNet, ти користуєшся результатом обчислення, яке не повториш.
Друге: критерій пошуку вирішує все. MobileNet-v3 шукали не за кількістю множень, а за заміряним часом на реальному телефоні — і саме тому вона вийшла іншою, ніж мережі, підібрані за FLOPs. Це той самий висновок розділу 06, тільки зроблений авторами архітектури: оптимізуєш не ту величину — отримаєш не ту мережу.
ВибірЩо обирати під задачу
Питання «яка архітектура найкраща» відповіді не має. Питання «яка вкладається в мій бюджет» — має завжди.
| Куди ставимо | Що тисне насправді | З чого починати |
|---|---|---|
| Сервер, пакетна обробка | Практично нічого: памʼять є, час є | Найточніше, що витримає бюджет грошей: ResNet50, Inception-v3 |
| Сервер, відповідь на запит | Затримка на один запит; вага файлу не тисне | ResNet18 або EfficientNet-B0, і обовʼязково з заміром часу |
| Телефон, кадр із камери | Усе одразу: вага, памʼять, 33 мс, нагрів | MobileNet-v3 із множником ширини під свій бюджет |
| Браузер | Розмір завантаження понад усе | MobileNet-v3-small — 10 МБ проти 553 МБ у VGG16 |
Найчастіша помилка тут — обрати архітектуру за таблицею з чужої статті: там міряли на іншому залізі й, найімовірніше, у FLOPs. Правильний порядок інший: назви свій бюджет числом, візьми дві-три кандидатури, заміряй їх на своєму залізі — і лише потім дивись на точність серед тих, що вклались.
Хто вкладається в бюджет
Обери сценарій — і подивись, скільки з девʼяти архітектур узагалі можна розглядати. Повзунки рухають межі, якщо твій бюджет інший.
ПідсумокЩо лишається в голові
Ця тема не додала жодного нового способу зробити мережу точнішою. Вона додала інше — уміння називати ціну.
| Ідея | Що вона насправді дає |
|---|---|
| Три різні «дешевше» | Параметри, множення й час не збігаються навіть у порядку: AlexNet другий за параметрами й сьомий за часом |
| Модуль Inception | Не обирати розмір ядра, а взяти всі й склеїти по каналах |
| Пляшкове горло 1×1 | Стиснути канали перед дорогим ядром: 2.4 раза — але лише коли каналів багато; на 64 каналах горло шкодить |
| Роздільна згортка | Розділити «де» і «що». Параметрів менше
в 8-9 разів, межа економії дорівнює k² |
| Множники ширини й роздільності | Дві ручки на різні обмеження; одна одну не замінюють |
| Складене масштабування | Рости за трьома осями разом. Пропорція знайдена пошуком, а не виведена |
І один висновок, важливіший за всі шість. Економія параметрів — не економія часу. Ми бачили це тричі: роздільна згортка дала 8.24× за параметрами й 4.45× за часом на одному шарі; 6.19× за параметрами й 1.08× за часом на нашій мережі; а мережі на роздільних згортках використовують процесор у три-сім разів гірше за грубу VGG16. Кількість множень — міра роботи, а не швидкості, і між ними стоїть памʼять.
Місток до наступної теми
Девʼять архітектур із наших таблиць різняться між собою в 54 рази за вагою й у 40 разів за часом. Але є одне, у чому вони не різняться зовсім: усі вони навчені на ImageNet — базі з мільйона зображень, де кожне має мітку, поставлену людиною.
Скільки коштує та мітка, ми рахували ще в темі 02: розмітка — найдорожча частина будь-якого проєкту компʼютерного зору. Ми навчилися робити мережу вдесятеро дешевшою в обчисленнях — і не зрушили найбільшу статтю витрат ані на копійку.
Наступна тема — 16. Самонаглядове навчання — про спробу прибрати саме її. Питання там просте на слух і незручне по суті: чи може мережа навчитися дивитись на зображеннях, яких ніхто не розмічав?