Дії у відео

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

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

Це і є задача теми: розпізнавання дій (action recognition). Вхід — не зображення, а відрізок відео; відповідь — назва дії. Формально це та сама класифікація, з якою ми почали блок 2, тільки вхід має на один вимір більше. Практично — це задача, де найлегше сама себе обдурити, і саме про це варто говорити насамперед.

РозривОдна мить проти відрізка

Візьми будь-який популярний відеонабір і подивись, які там класи: «плавання», «гра на гітарі», «нарізання овочів». Тепер прибери час — лиши по одному випадковому кадру з кожного кліпу й навчи звичайну картинкову мережу з теми 14. Вона набере несподівано багато.

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

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

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

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

Одна мить нічого не каже

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

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

НабірНабір, у якому один кадр марний за побудовою

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

ривок ++++--+--+-- затинка +++-++---+-- сходи ++-++-++----

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

перша

У кожної дії рівно шість кроків уперед і шість назад. Модель, яка просто рахує, скільки було руху в кожен бік, не дізнається нічого.

друга

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

третя

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

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

Скільки кадрів треба побачити разом

Перелік вікон завдовжки n для кожної з трьох дій. Поки переліки збігаються, ознака з таким рецептивним полем сліпа.

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

Найдешевший часКадри як канали

Найпростіший спосіб згодувати мережі відрізок відео — не міняти мережу взагалі. Кольоровий знімок ми вже подаємо як три канали (тема 01). Ніщо не заважає подати дванадцять кадрів як дванадцять каналів: перший шар просто матиме in_channels=12, і на цьому все.

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

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

Кадр у каналі проти ядра, що ковзає

Ліворуч — кадри як канали: кожен кадр має власні ваги. Праворуч — Conv3d: одне ядро проходить усі положення в часі.

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

ЧесністьПеремішування кадрів як тест

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

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

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

Порядок кадрів і те, що з нього лишається

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

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

Третій вимірТривимірна згортка

Другий спосіб додати час — дати ядру третій вимір. Звичайне ядро 3×3 ковзає по висоті й ширині. Ядро 3×3×3 ковзає ще й по часу: воно накриває три сусідні кадри одразу й дає одне число.

y[t, i, j] = Στ Σu Σv w[τ, u, v] · x[t+τ, i+u, j+v]

Розберемо запис. x — вхідний кліп, у нього три індекси: номер кадру t, рядок i, стовпець j. w — ядро, таблиця з 3×3×3 = 27 чисел. Знак Σ (грецька велика «сигма») означає «склади все, що далі, перебравши індекс під ним»; тут таких сум три — по часу, по висоті й по ширині. Тобто щоб отримати одне число на виході, ми беремо двадцять сім значень із трьох сусідніх кадрів, множимо кожне на свою вагу й складаємо. Тривимірна згортка вже є в глосарії (тривимірна згортка).

Порахуймо руками найменший приклад. Хай ядро по часу має три ваги (−1, 0, +1), а по простору просто підсумовує яскравість кадру. Тоді на кожному кроці воно рахує «наскільки яскравіше стало через два кадри». Візьмімо три сусідні числа: 5, 7, 9. Виходить −1·5 + 0·7 + 1·9 = 4. Ті самі три ваги на числах 9, 7, 5 дають −1·9 + 0·7 + 1·5 = −4. Одне й те саме ядро видає додатне число на зростанні й відʼємне на спаданні — ось так згортка по часу й перетворюється на детектор напрямку руху, без жодного окремого механізму.

Головна відмінність від «кадрів як каналів» — поділ ваг (спільні ваги) уздовж часу. Те саме ядро w прикладається до кадрів 1-3, потім до 2-4, потім до 3-5. Мережа вчиться одному детектору руху й безкоштовно вміє знаходити його будь-де в кліпі. Це та сама ідея, що зробила згортку кращою за повнозвʼязний шар на зображеннях, тільки застосована до часу.

Ціна теж та сама, що завжди: Conv3d(64, 64, 3) має 110 656 ваг проти 36 928 у Conv2d(64, 64, 3), тобто втричі більше. Порахуймо звідки: у двовимірному ядрі 64 · 64 · 9 = 36 864 ваги, у тривимірному 64 · 64 · 27 = 110 592, і це рівно втричі; ще по одному зсуву на кожен із 64 вихідних каналів дають надруковані 36 928 і 110 656. А обчислень — теж утричі, бо на кожну пару каналів припадає 27 множень замість 9.

ТаблоЩо дав час насправді

Тепер порівняння. Кістяк у всіх моделей однаковий: два згорткові шари, ширина 8 і 16, простір зводиться середнім. Різниця лише в тому, звідки береться час. Три зерна на кожну точку, AdamW, 240 навчальних кліпів і 300 перевірних.

модельвагсправжній порядоккадри перемішані
один кадр1 2990.3433 ±0.01250.3078 ±0.0325
кадри як канали2 0911.0000 ±0.00000.3589 ±0.0157
Conv3d + avg по часу3 7470.3978 ±0.10620.3078 ±0.0042
Conv3d + max по часу3 7470.8211 ±0.08440.3267 ±0.0163
Conv3d + розгортка часу4 2751.0000 ±0.00000.3489 ±0.0181
випадкове вгадування—0.33330.3333

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

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

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

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

КінцівкаЩо стоїть після часового шару

У переважній більшості прикладів із Conv3d після згорток стоїть AdaptiveAvgPool3d(1). Цей рядок зводить карту ознак до одного числа на канал — усереднюючи по висоті, по ширині і по часу. Він виглядає технічним: «зведімо все до вектора, далі лінійний шар». Насправді він вирішує долю всієї архітектури.

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

На нашому наборі це смертельно за побудовою. Три дії мають однакову кількість кроків уперед і назад і однакову статистику трикадрових вікон. Усе, що можна дізнатись, усереднивши по часу коротке вікно, у них однакове.

Три способи прочитати часову вісь

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

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

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

читач часу поверх тих самих ознакточність
середнє по часу0.4000
максимум по часу0.9533
розгортка часу1.0000

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

Що з цього випливає для чужих порівнянь. Коли стаття пише «3D-згортка не дала переваги над 2D», перше питання — не про згортки. Перше питання: що стояло після них. Глобальне усереднення по часу — настройка за замовчуванням у половині реалізацій, і воно здатне зʼїсти весь виграш часового шару ще до того, як почнеться порівняння архітектур.

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

ВинагородаКоли поділ ваг уздовж часу окупається

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

Побудуємо набір, де зсув є. Дії три, і кожна — слово з шести кроків: гойдалка (+++---), тремтіння (+-+-+-) і відкат (---+++). Шість кроків — це сім положень кулі, тобто дія займає сім кадрів. Кліп же має шістнадцять кадрів, і дія починається в довільний момент: сім кадрів дії всередині шістнадцятьох дають 16 − 7 + 1 = десять можливих початків. До дії й після неї куля стоїть. Усі три слова повертають кулю точно туди, звідки вона почала (у кожному по три кроки в кожен бік), тож ані перший, ані останній кадр класу не видають.

модельдія посерединідія в довільний моментвтрата
один кадр0.3544 ±0.00680.3267 ±0.0152—
кадри як канали0.8456 ±0.00160.6656 ±0.03010.1800
Conv3d + max по часу0.9100 ±0.01190.9056 ±0.02220.0044

Ось де винагорода. Коли дія стоїть посередині, обидві моделі працюють, і Conv3d уже трохи попереду. Щойно дія починає плавати в часі, «кадри як канали» втрачають 0.1800, а Conv3d — лише 0.0044. Купи по трьох зернах не перетинаються: найгірше зерно Conv3d (0.8867) вище за найкраще зерно стеку (0.7000). За критерієм самого курсу (тема 26) це справжня різниця, а не шум.

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

І звернімо увагу на кінцівку в цій таблиці: тут Conv3d читає час максимумом, а не середнім. Це не випадковість. «Дія трапилась хоч раз десь у кліпі» — це саме максимум по часу, і саме така кінцівка пасує до задачі, де момент дії неважливий. На наборі А, де важливий порядок фаз, та сама кінцівка програвала розгортці. Одна архітектура, дві задачі, дві різні правильні кінцівки.

Два потокиВигляд і рух окремими гілками

Історично першою вдалою відеоархітектурою був не Conv3d, а двопотокова мережа (two-stream). Думка в неї така: вигляд і рух — це два різні джерела, тож хай кожне має власну гілку. Перша гілка дивиться на кадр і відповідає на питання «що тут зображено». Друга дивиться не на кадри, а на оптичний потік (optical flow) — поле зсувів між сусідніми кадрами, яке вводить тема 34. Наприкінці відповіді двох гілок складають.

Причина, чому це працювало, приземлена: потік рахує OpenCV, а не мережа. Гілці руху не треба вчитися бачити рух — їй одразу дають готове поле зсувів. У нашому замірі потік рахує calcOpticalFlowFarneback між кожною парою сусідніх кадрів: дванадцять кадрів дають одинадцять пар, а кожна пара — дві карти (зсув по горизонталі й по вертикалі). Разом 11 · 2 = 22 карти, і саме вони подаються гілці руху як 22 канали.

гілкавагточність
лише вигляд1 2990.3433 ±0.0125
лише рух2 8111.0000 ±0.0000
два потоки4 1071.0000 ±0.0000

На нашому наборі гілка руху не «додає трохи» — вона несе все: +0.6567 понад гілку вигляду. Гілка вигляду сама по собі не може нічого, бо ми так і збудували набір; разом із рухом вона нічого не псує, але й не додає.

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

Що бачить кожна гілка

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

зсув (u, v), px—
напрям—
точність обраної гілки—
Що показує поле. Куля зміщується на один крок кільця за кадр, і поле зсувів має два згустки: там, звідки куля пішла, і там, куди прийшла. Гілка вигляду цього зсуву не бачить взагалі — вона дивиться на один кадр.

Ціна ядра(2+1)D: той самий обсяг за іншу ціну

Ядро 3×3×3 дороге. Але його можна розкласти так само, як MobileNet розклав звичайну згортку: спершу згортка тільки по простору, 1×3×3, потім згортка тільки по часу, 3×1×1, а між ними — проміжні канали. Дві дешеві операції накривають ту саму область 3×3×3, що й одна дорога. Так влаштована архітектура R(2+1)D.

Порахуймо. Нехай на вході Cin каналів, на виході Cout, а проміжних каналів M:

Conv3d: C_in · C_out · 27 просторова: C_in · M · 9 (ядро 1×3×3) часова: M · C_out · 3 (ядро 3×1×1)

Розклад дешевший, поки M малий, і дорожчий, коли великий. Цікава точка — рівність. Прирівняймо й розвʼяжімо відносно M:

крок 1

Хочемо, щоб ваг у розкладі було рівно стільки ж, скільки в цілому ядрі:

C_in · M · 9 + M · C_out · 3 = C_in · C_out · 27
крок 2

Ліворуч виносимо M за дужки — воно є в обох доданках:

M · (9 · C_in + 3 · C_out) = 27 · C_in · C_out
крок 3

Ділимо обидві частини на дужку й дістаємо шуканий розмір:

M* = 27 · C_in · C_out ÷ (9 · C_in + 3 · C_out)
перевірка на числах

Для 64 → 64: зверху 27 · 64 · 64 = 110 592, знизу 9 · 64 + 3 · 64 = 768. Ділимо: 110 592 ÷ 768 = 144. Саме таке значення й стоїть у статті R(2+1)D для цього шару.

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

Скільки коштує розкласти ядро

Посунь проміжний розмір M і подивись, де розклад дешевший за ціле ядро, а де вже дорожчий.

ваг у Conv3d—
ваг у розкладі—
рівновага M*—
частка ціни—
Три стовпчики — звичайна двовимірна згортка, розклад (2+1)D при обраному M і ціле ядро 3×3×3. Пунктир позначає рівновагу: при M = M* середній стовпчик зрівнюється з правим.

ГотовеВідеомережі, які вже написані

torchvision уміє збудувати шість відеоархітектур без жодного байта з мережі — досить weights=None. Збудуймо всі шість і порахуймо ваги:

архітектурапараметрівщо всередині
s3d8 320 048розділені згортки в дусі Inception
mc3_1811 695 4403D тільки в перших блоках, далі 2D
swin3d_t28 158 070вікна Swin, розширені на час
r2plus1d_1831 505 325розклад (2+1)D із цієї теми
r3d_1833 371 472ResNet-18, у якої всі ядра тривимірні
mvit_v2_s34 537 744трансформер із пулінгом уваги

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

Мірна рейка. Більшість цих архітектур — знайомі картинкові мережі, яким додали час. Тож поставмо поруч ту саму ResNet-18 із теми 14, тільки двовимірну: у неї 11 689 512 ваг. Порівнювати треба тіла, без класифікатора: двовимірна мережа має голову на тисячу класів ImageNet (512 · 1000 + 1000 = 513 000 ваг), а відеомережі — на чотириста класів Kinetics (512 · 400 + 400 = 205 200). Тіло двовимірної ResNet-18 — це 11 689 512 − 513 000 = 11 176 512 ваг, і саме з ним ми зіставляємо решту.

модельтіло, вагтіло ÷ тіло ResNet-18 (2D) що зробили з часом
ResNet-18 (2D)11 176 5121.00нічого, це рейка
mc3_1811 490 2401.03тривимірні лише перші блоки
r3d_1833 166 2722.97тривимірні всі ядра
r2plus1d_1831 300 1252.80ті самі ядра, розкладені на 1×3×3 і 3×1×1
чому 2.97, а не рівно 3

Розділ 05 порахував: ядро 3×3×3 має рівно втричі більше ваг за 3×3. r3d_18 робить тривимірними всі ядра 3×3 — але не ті, що вже й так завширшки один піксель. У ResNet є короткі звʼязки (тема 14), і на трьох із них стоїть згортка 1×1, яка міняє кількість каналів: 64 → 128, 128 → 256, 256 → 512. Разом це 8 192 + 32 768 + 131 072 = 172 032 ваги, і в тривимірній мережі вони лишаються тими самими 1×1×1. Звідси й точне число: 172 032 + (11 166 912 − 172 032) · 3 = 33 156 672 ваги в згортках — рівно стільки, скільки має r3d_18. Множник 2.97 — це і є «втричі, крім тих 172 тисяч і кількох тисяч ваг нормалізації, які не множаться взагалі».

чому 1.03

mc3_18 ставить тривимірні ядра лише на початку — у першому шарі й у першій групі блоків, — а далі йде звичайна двовимірна мережа. Ваги в згорткових мережах живуть переважно в глибоких шарах: там каналів 256 і 512, а на початку — 64. Тож потроєння дешевого початку коштує 18 816 (перший шар) + 294 912 (перша група) = 313 728 ваг — рівно стільки й додається до тіла: 11 176 512 + 313 728 = 11 490 240. Це 2.8 % доплати за те, що початок мережі бачить час.

чому 2.80, майже як у r3d_18

Розділ 10 показав: при M = M* розклад (2+1)D коштує стільки ж, скільки ціле ядро. r2plus1d_18 — та сама r3d_18 із розкладеними ядрами, і її тіло становить 31 300 125 ÷ 33 166 272 = 0.94 від нерозкладеного. Шість відсотків — це не економія, а похибка добору M у статті. Купують тут, як ми вже знаємо, не ціну, а подвоєне число нелінійностей.

чому s3d найлегша

s3d не з родини ResNet: у неї тіло в дусі Inception, де паралельні гілки з вузькими ядрами, і всі згортки розділені на просторову й часову. Обидва прийоми ріжуть ядра, тож 8 320 048 ваг — менше навіть за двовимірну ResNet-18 цілком, з її 11 689 512.

чому трансформери інші

swin3d_t і mvit_v2_s ядер не мають узагалі — у них ваги сидять у матрицях уваги й у повнозвʼязних шарах, а ціну задають розмірність токена — довжина вектора, яким описується кожен патч, — і кількість блоків (тема 20). Тому додавання часу майже не робить їх дорожчими: вісь часу просто додається до набору патчів, а ваг від цього не більшає. Їхні 28 і 34 мільйони — це ціна трансформера, а не ціна часу.

Отже стовпчик читається так: дорожчає не «тривимірність», а те, скільки глибоких шарів ти зробив тривимірними. Одна й та сама ідея коштує 2.8 % у mc3_18 і 197 % у r3d_18 — залежно від того, де саме її застосували.

Але «найлегша» не означає «найневибагливіша». r3d_18 спокійно рахує вхід (1, 3, 16, 112, 112) — шістнадцять кадрів по 112 пікселів. А s3d на тому самому вході падає:

RuntimeError: input image (T: 2 H: 3 W: 3) smaller than kernel size (kT: 2 kH: 7 kW: 7)

Причина видна прямо в тексті помилки: у s3d перше ядро завширшки сім пікселів, і після кількох зменшень від 112 лишається карта 3×3, у яку ядро 7×7 не влазить. Йому треба вхід 224×224. Мораль проста: розмір входу — частина архітектури, а не налаштування, і найлегша модель може виявитись найвибагливішою.

Ще одна практична обставина. Усі шість архітектур будуються офлайн, але ваги до них так не дістати: вони важать сотні мегабайтів і живуть у мережі. Для відео це болючіше, ніж для картинок: кліп важить у десятки разів більше за кадр, тож і навчання з нуля коштує відповідно. А наші власні заміри показали, чому складніша архітектура сама по собі — не гарантія: на наборі А найпростіші «кадри як канали» з 2 091 ваг дали 1.0000, а майже вдвічі важча Conv3d з усередненням по часу — 0.3978.

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

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

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

Коротко, у порядку важливості.

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