Уяви два знімки. На першому людина стоїть посеред басейну по груди у воді. На другому — та сама людина в тій самій позі. Що вона робить: пливе, виходить чи щойно стрибнула? З одного знімка не сказати. А тепер уяви, що тобі показали дванадцять кадрів підряд — і питання зникло само.
Це і є задача теми: розпізнавання дій (action recognition). Вхід — не зображення, а відрізок відео; відповідь — назва дії. Формально це та сама класифікація, з якою ми почали блок 2, тільки вхід має на один вимір більше. Практично — це задача, де найлегше сама себе обдурити, і саме про це варто говорити насамперед.
РозривОдна мить проти відрізка
Візьми будь-який популярний відеонабір і подивись, які там класи: «плавання», «гра на гітарі», «нарізання овочів». Тепер прибери час — лиши по одному випадковому кадру з кожного кліпу й навчи звичайну картинкову мережу з теми 14. Вона набере несподівано багато.
Причина проста й неприємна. «Плавання» впізнається по басейну. «Гра на гітарі» — по гітарі. «Нарізання овочів» — по кухні й дошці. Обстановка вирішує задачу замість руху, і жодного руху для цього не треба.
Практика галузі це визнає й лікує напівзаходами. Найчастіший — так званий однокадровий базовий рівень: перш ніж хвалитись відеомоделлю, автори навчають ту саму мережу на одному випадковому кадрі й показують її число поруч. Це чесно, але не розвʼязує задачу: якщо однокадровий рівень дає 0.72, то весь простір для розмови про час — це проміжок від 0.72 до одиниці, і всі висновки про архітектури живуть у цьому вузькому проміжку.
Вихід один: побудувати набір, у якому один кадр не дає нічого. Не «мало», не «на нашу думку небагато», а рівно стільки, скільки дає підкидання монети. Тоді все, що модель набере понад випадок, узялося з часу — іншого джерела просто немає.
Одна мить нічого не каже
Три дії на кільцевій доріжці. Верхній ряд — де куля на обраному кадрі, нижній — як розподілені кути кулі на цьому кадрі по всій вибірці.
НабірНабір, у якому один кадр марний за побудовою
Пристрій набору такий. Куля їздить по кільцевій доріжці, її положення —
це кут. Кожен кліп починається з випадкового кута, рівномірного на всьому колі.
Дія — це слово з дванадцяти кроків, де + означає крок за годинниковою
стрілкою, а − проти:
Три властивості цього набору тримають усю тему, і кожну можна перевірити прямо на трьох словах вище, олівцем.
У кожної дії рівно шість кроків уперед і шість назад. Модель, яка просто рахує, скільки було руху в кожен бік, не дізнається нічого.
Усі три дії проходять той самий набір положень. Це видно олівцем:
на дванадцяти кадрах кліпу куля кожної дії стоїть на найнижчому положенні
один раз, на наступному — тричі, на середньому — чотири рази, далі знову
тричі й один раз. Три різні порядки, один і той самий набір. Тож
усереднений по часу кадр у трьох дій однаковий: заміряна різниця дорівнює
0.0000000298, тобто рівно нуль у межах точності float.
Збігаються не лише окремі положення, а й усі вікна з двох і трьох кадрів. Різниця зʼявляється тільки на вікнах у чотири кадри. Це важливо далі: згортка з ядром 3 по часу бачить рівно трикадрове вікно.
Слово «вікно» тут означає ось що: беремо підряд n положень кулі, віднімаємо від них перше (бо абсолютний кут усе одно випадковий) і дивимось, які такі відрізки й скільки разів трапляються в дії. Якщо у трьох дій ці переліки збігаються, то будь-яка ознака, чиє рецептивне поле вздовж часу дорівнює n кадрам, у них однакова — незалежно від того, наскільки розумно її придумали.
Скільки кадрів треба побачити разом
Перелік вікон завдовжки n для кожної з трьох дій. Поки переліки збігаються, ознака з таким рецептивним полем сліпа.
Найдешевший часКадри як канали
Найпростіший спосіб згодувати мережі відрізок відео — не міняти мережу взагалі.
Кольоровий знімок ми вже подаємо як три канали
(тема 01). Ніщо не заважає подати
дванадцять кадрів як дванадцять каналів: перший шар просто матиме
in_channels=12, і на цьому все.
Прийом виглядає грубо, але думка за ним чесна: перший згортковий шар і так
змішує канали між собою. Якщо кадри лежать у каналах, то ядро 3×3
одним множенням порівнює точку на першому кадрі з тією самою точкою на дванадцятому.
Різниця між кадрами — це просто різниця каналів, і мережа може її вивчити.
Ціна теж своя. Кожен кадр міцно привʼязаний до свого каналу, а отже до своїх ваг. Кадр номер три має власний набір коефіцієнтів, і те, чого мережа навчилася на третьому кадрі, ніяк не переноситься на восьмий. Поки дія стоїть на тому самому місці в кліпі, це не заважає. Щойно дія починає плавати в часі — стає дорого.
Кадр у каналі проти ядра, що ковзає
Ліворуч — кадри як канали: кожен кадр має власні ваги. Праворуч —
Conv3d: одне ядро проходить усі положення в часі.
Conv3d ділить ваги вздовж часу: одне ядро обслуговує всі положення,
і копія завжди одна.ЧесністьПеремішування кадрів як тест
Припустімо, модель набрала високу точність на відео. Звідки знати, що вона читає рух, а не знайшла обхідний шлях? Є перевірка, яка коштує три рядки коду й працює на будь-якій відеомоделі.
Перемішай кадри всередині кожного кліпу — і в навчальній частині, і в перевірній. Набір кадрів лишається той самий, зникає тільки порядок. Модель, яка справді читає рух, має провалитись до випадку. Модель, яка вчепилася за обстановку чи за окремий характерний кадр, лишиться майже там, де була.
Порядок кадрів і те, що з нього лишається
Той самий кліп у справжньому порядку й перемішаний. Унизу — заміряна точність тієї самої мережі на обох варіантах.
Третій вимірТривимірна згортка
Другий спосіб додати час — дати ядру третій вимір.
Звичайне ядро 3×3 ковзає по
висоті й ширині. Ядро 3×3×3 ковзає ще й по часу: воно накриває
три сусідні кадри одразу й дає одне число.
Розберемо запис. 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 299 | 0.3433 ±0.0125 | 0.3078 ±0.0325 |
| кадри як канали | 2 091 | 1.0000 ±0.0000 | 0.3589 ±0.0157 |
| Conv3d + avg по часу | 3 747 | 0.3978 ±0.1062 | 0.3078 ±0.0042 |
| Conv3d + max по часу | 3 747 | 0.8211 ±0.0844 | 0.3267 ±0.0163 |
| Conv3d + розгортка часу | 4 275 | 1.0000 ±0.0000 | 0.3489 ±0.0181 |
| випадкове вгадування | — | 0.3333 | 0.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 |
Ось і відповідь. Згортки знайшли все, що треба: розгорнувши часову вісь, навіть проста лінійна модель розділяє класи ідеально. Ті самі числа, стиснуті середнім по часу, не розділяють нічого. Часовий шар працював як слід — його знецінив рядок, що стоїть після нього.
Чесна межа цього твердження теж є. Наша гарантія «середнє сліпе» доведена для
ознак, чиє рецептивне поле вздовж часу не перевищує трьох кадрів. У мережі з двох
згорткових шарів воно дорівнює пʼяти кадрам, а на пʼятикадрових вікнах дії вже
розрізняються. Тому
max по часу й набирає 0.8211, а не рівно випадок: він частково
дотягується до довших вікон. Але навіть із цією форою він програє розгортці.
ВинагородаКоли поділ ваг уздовж часу окупається
Отже, на наборі А тривимірна згортка не виграла нічого. Це чесний результат, і пояснення в нього просте: дія там займає весь кліп і починається з першого кадру. Кожен кадр має свій номер, «кадри як канали» привʼязують кадр до каналу — і цього досить. Поділ ваг уздовж часу купує інваріантність до зсуву в часі, а за неї нема чого платити, поки зсуву немає.
Побудуємо набір, де зсув є. Дії три, і кожна — слово з шести кроків:
гойдалка (+++---), тремтіння (+-+-+-) і відкат
(---+++). Шість кроків — це сім положень кулі, тобто дія займає
сім кадрів. Кліп же має шістнадцять кадрів, і дія починається
в довільний момент: сім кадрів дії всередині шістнадцятьох дають
16 − 7 + 1 = десять можливих початків. До дії й після неї куля стоїть.
Усі три слова повертають кулю точно туди, звідки вона почала (у кожному по три
кроки в кожен бік), тож ані перший, ані останній кадр класу не видають.
| модель | дія посередині | дія в довільний момент | втрата |
|---|---|---|---|
| один кадр | 0.3544 ±0.0068 | 0.3267 ±0.0152 | — |
| кадри як канали | 0.8456 ±0.0016 | 0.6656 ±0.0301 | 0.1800 |
| Conv3d + max по часу | 0.9100 ±0.0119 | 0.9056 ±0.0222 | 0.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 299 | 0.3433 ±0.0125 |
| лише рух | 2 811 | 1.0000 ±0.0000 |
| два потоки | 4 107 | 1.0000 ±0.0000 |
На нашому наборі гілка руху не «додає трохи» — вона несе все: +0.6567 понад гілку вигляду. Гілка вигляду сама по собі не може нічого, бо ми так і збудували набір; разом із рухом вона нічого не псує, але й не додає.
На справжніх наборах співвідношення протилежне: вигляд там сильний, і рух додає одиниці відсотків. Наш замір показує другий бік тієї самої монети — коли вигляд вимкнено за побудовою, увесь результат тримається на русі. Разом ці два заміри й пояснюють, чому двопотоковий підхід так довго тримався: він розділяє джерела, які на справжньому відео змішані, і дає побачити внесок кожного.
Що бачить кожна гілка
Ліворуч — кадр, який дістає гілка вигляду. Праворуч — поле зсувів між обраним кадром і наступним, тобто те, що дістає гілка руху.
Ціна ядра(2+1)D: той самий обсяг за іншу ціну
Ядро 3×3×3 дороге. Але його можна розкласти так само, як
MobileNet розклав звичайну
згортку: спершу згортка тільки по простору, 1×3×3, потім згортка тільки
по часу, 3×1×1, а між ними — проміжні канали. Дві дешеві операції
накривають ту саму область 3×3×3, що й одна дорога. Так
влаштована архітектура R(2+1)D.
Порахуймо. Нехай на вході Cin каналів, на виході Cout, а проміжних каналів M:
Розклад дешевший, поки M малий, і дорожчий, коли великий. Цікава точка — рівність. Прирівняймо й розвʼяжімо відносно M:
Хочемо, щоб ваг у розкладі було рівно стільки ж, скільки в цілому ядрі:
Ліворуч виносимо M за дужки — воно є в обох доданках:
Ділимо обидві частини на дужку й дістаємо шуканий розмір:
Для 64 → 64: зверху 27 · 64 · 64 = 110 592, знизу
9 · 64 + 3 · 64 = 768. Ділимо:
110 592 ÷ 768 = 144. Саме таке значення й стоїть у
статті R(2+1)D для цього шару.
І тут головне. Коли M дібрано так, розклад
не економить нічого: ваг порівну, обчислень порівну. Виграш беруть з іншого
місця — нелінійностей удвічі більше. ReLU стоїть і після
просторової згортки, і після часової, тож той самий бюджет ваг дає глибшу нелінійну
функцію. Це рідкісний випадок, коли архітектурний прийом продають не як економію, а
як спосіб витратити ті самі гроші розумніше.
Скільки коштує розкласти ядро
Посунь проміжний розмір M і подивись, де розклад дешевший за ціле ядро, а де вже дорожчий.
ГотовеВідеомережі, які вже написані
torchvision уміє збудувати шість відеоархітектур без жодного байта з
мережі — досить weights=None. Збудуймо всі шість і порахуймо ваги:
| архітектура | параметрів | що всередині |
|---|---|---|
| s3d | 8 320 048 | розділені згортки в дусі Inception |
| mc3_18 | 11 695 440 | 3D тільки в перших блоках, далі 2D |
| swin3d_t | 28 158 070 | вікна Swin, розширені на час |
| r2plus1d_18 | 31 505 325 | розклад (2+1)D із цієї теми |
| r3d_18 | 33 371 472 | ResNet-18, у якої всі ядра тривимірні |
| mvit_v2_s | 34 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 512 | 1.00 | нічого, це рейка |
| mc3_18 | 11 490 240 | 1.03 | тривимірні лише перші блоки |
| r3d_18 | 33 166 272 | 2.97 | тривимірні всі ядра |
| r2plus1d_18 | 31 300 125 | 2.80 | ті самі ядра, розкладені на 1×3×3 і 3×1×1 |
Розділ 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 тисяч і кількох тисяч ваг нормалізації, які не множаться
взагалі».
mc3_18 ставить тривимірні ядра лише на початку — у першому
шарі й у першій групі блоків, — а далі йде звичайна двовимірна мережа. Ваги
в згорткових мережах живуть переважно в глибоких шарах: там каналів
256 і 512, а на початку — 64. Тож потроєння дешевого початку коштує
18 816 (перший шар) + 294 912 (перша група) = 313 728 ваг — рівно
стільки й додається до тіла: 11 176 512 + 313 728 = 11 490 240. Це 2.8 %
доплати за те, що початок мережі бачить час.
Розділ 10 показав: при
M = M* розклад (2+1)D
коштує стільки ж, скільки ціле ядро. r2plus1d_18 — та сама
r3d_18 із розкладеними ядрами, і її тіло становить
31 300 125 ÷ 33 166 272 = 0.94 від нерозкладеного. Шість відсотків —
це не економія, а похибка добору M у статті. Купують
тут, як ми вже знаємо, не ціну, а подвоєне число нелінійностей.
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 на тому самому вході падає:
Причина видна прямо в тексті помилки: у s3d перше ядро завширшки
сім пікселів, і після кількох зменшень від 112 лишається карта 3×3, у яку ядро 7×7
не влазить. Йому треба вхід 224×224. Мораль проста: розмір входу — частина
архітектури, а не налаштування, і найлегша модель може виявитись
найвибагливішою.
Ще одна практична обставина. Усі шість архітектур будуються офлайн, але
ваги до них так не дістати: вони важать сотні мегабайтів і живуть у мережі.
Для відео це болючіше, ніж для картинок: кліп важить у десятки разів більше за
кадр, тож і навчання з нуля коштує відповідно. А наші власні заміри показали, чому
складніша архітектура сама по собі — не гарантія: на наборі А найпростіші «кадри як
канали» з 2 091 ваг дали 1.0000, а майже вдвічі важча Conv3d з
усередненням по часу — 0.3978.
Тема 12 заміряла, як поводиться чуже передтреноване тіло залежно від того, скільки в тебе власних розмічених прикладів: воно виграє, поки їх мало, і починає програвати, коли їх стає багато. Відео майже завжди тримає тебе в лівій частині тієї кривої — розмітити тисячу кліпів набагато дорожче, ніж тисячу знімків. Але висновок теми 12 має й другу половину, і саме її тут найлегше пропустити: перш ніж брати передтреновану модель, навчи маленьку з нуля й запиши число. Наші власні числа показують, навіщо: без такої точки відліку «складніша архітектура покращила результат» не означає нічого.
ПідсумокЩо з цього забрати
Коротко, у порядку важливості.
- Перевіряй, чи потрібен твоїй моделі час. Перемішай кадри й навчи заново. Якщо число не впало — час не потрібен, хоч би скільки тривимірних згорток стояло всередині. Це три рядки коду й найдешевша перевірка чесності відеомоделі, яку взагалі можна зробити.
- Дивись, що стоїть після часового шару. Глобальне усереднення по часу стирає порядок фаз — рівно те, що часовий шар щойно знайшов. Один і той самий набір ознак дав 0.4000 під середнім і 1.0000 під розгорткою.
- Кінцівку обирай під задачу. «Трапилось хоч раз» — це максимум по часу. «У якому порядку йшли фази» — це розгортка. «Скільки всього було» — середнє, і саме воно за замовчуванням стоїть майже скрізь.
- Поділ ваг уздовж часу купує інваріантність до зсуву. Він окупається, коли дія може статись у будь-який момент кліпу, і не окупається, коли кліпи вирізані по дії.
- Розклад (2+1)D — не про економію. При M = M* ваг порівну з цілим ядром; купують за них подвоєне число нелінійностей.
Блок 7 додає до знімка нові виміри. Ця тема додала час — і показала, що головна складність не в тому, щоб його додати, а в тому, щоб не втратити його всередині власної мережі. Тема 36 додасть інший вимір — глибину. Там теж буде замір, який руйнує інтуїцію: два знімки різних за розміром сцен збігаються побітово, і жодна мережа не відновить масштаб із одного кадру, бо його там немає.