Kaggle: аналіз місцевості Амазонки за супутниковими знімками

Нещодавно на kaggle.com проходило змагання Planet understanding the amazon from space

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

Завдання

Власне, з чого починається кожне змагання - з постановки проблеми і метрики якості. Завдання полягало в наступному: були дані знімки із супутників місцевості Амазонки, і для кожного знімка потрібно було проставити лейбли: дорога, річка, поле, ліс, хмари, ясне небо і так далі (всього 17 штук). Причому на одній картинці могли одночасно перебувати кілька класів. Як видно, крім типу місцевості, були присутні ще й класи, що відносяться до погодних умов, і, що здається логічним, погода на знімку може бути тільки одна. Ну не може бути одночасно і ясно, і хмарно. При вирішенні змагання я не дивився очима на дані, сподіваючись, що машина сама розбереться, хто чий брат, тому довелося покопатися, щоб навести приклади зображень:

Baseline

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

Тепер трохи докладніше про процедуру доотримання терезів (у галузі це називається Fine-tune). Береться згорточна нейронна мережа, наприклад VGG16, навчена на датасеті ImceNet, що складається з декількох мільйонів картинок, передбачати один з 1000 класів. Ну добре, пророкує вона кішку, собаку, машину, ну і що? У нас то класи зовсім інші... А сенс у тому, що нижні шари нейромережі вже вміють розпізнавати такі базові компоненти картинки, як лінії і градієнти. Нам залишається лише видалити верхній шар з 1000 нейронів і поставити замість нього свій з 17 нейронів (саме стільки класів може бути на супутникових знімках).

Таким чином, нейромережа буде передбачати ймовірність кожного з 17 класів. Як за ймовірністю сказати, чи є конкретний клас на картинці? Найпростіша ідея - відсікати за єдиним порогом: якщо ймовірність більша, ніж, наприклад, 0.2, то клас вмикаємо, якщо менше - то не вмикаємо. Можна цей поріг підібрати для кожного класу окремо, і я не придумав нічого розумнішого, ніж незалежно (що звичайно ж неправда, поліпшення одного порогу може впливати на підбір іншого) перебирати, як їх називали в чаті, thresholds.

Сказано - зроблено. Результат - топ 90% лідерборда. Так, погано, але треба ж з чогось починати. І тут хочеться сказати про величезну перевагу змагань - форум, на якому купа людей, професіоналів і не дуже, працюють над однією проблемою, і навіть публікуються готові baseline'и. З обговорень я відразу зрозумів, що неправильно файнтюніл. Справа в тому, що потрібно дообучувати ваги в два етапи:

  1. «Заморозити» ваги всіх шарів, крім останнього (того, що з 17 нейронів) і навчати тільки його
  2. Після того, як лосс впаде до деякого значення і далі буде коливатися біля нього, «розморозити» всі ваги і навчати сітку цілком

Після цих нехитрих маніпуляцій я отримав хоч скільки-то прийнятну, на мій погляд, якість. Що робити далі?

Аугментації

Я чув, що для збагачення датасета можна робити аугментацію - деякі трансформації поданого на вхід нейромережі зображення. Ну дійсно, якщо ми повернемо картинку, то нікуди річка або дорога з неї не подінуться, але тепер для навчання у нас буде не одна картинка, а цілих дві. Я вирішив особливо не мудрити і повертав картинки тільки на кути, кратні 90 градусам, а так само відкеркалював їх. Таким чином, розмір тренувального датасета збільшився у 8 разів, що відразу в кращу сторону відбилося на якості.

Далі я подумав, чому б не робити те ж саме, але на фазі передбачення: взяти виходи мережі для перетворених картинок і усереднити? Здається, що так передбачення будуть більш стабільними. Спробував реалізувати, благо повертати картинки я вже навчився. Несподівано, але це реально спрацювало: при тій же архітектурі мережі я піднявся на лідерборді на 100 рядків. Тільки потім з чату я дізнався, що вже все придумано до мене і така техніка називається Test Time Augmentation.

Ансамбль 1

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

Які моделі можна комбінувати в завданні розпізнавання зображень? Очевидна ідея - різні архітектури згорточних нейромереж. Про їх топології можна почитати в цій статті.

Я використовував такі:

  • VGG16
  • VGG19
  • Resnet101
  • Resnet152
  • Inception_v3

Як ансамблювати? Для початку можна взяти і усереднити передбачення всіх мереж. Якщо вони не сильно корелюють і дають приблизно однакову якість, то, з мого досвіду, усереднення працює практично завжди. Спрацювало і зараз.

Команда

Худо-бідно, я дістався до кордону бронзової медальки. А тим часом, до кінця змагання залишався тиждень. Саме в цей час настає так званий merge deadline - момент, після якого забороняється об'єднання в команди. І ось, 20 хвилин до deadline'a, я думаю, а чому б мені, власне, не об'єднатися з ким-небудь в команду? Дивлюся на лідерборд, в надії знайти кого-небудь з чату біля себе. Але нікого немає online. Тільки asanakoy, який на той момент був на цілих 40 рядків вище мене. А що, а раптом? Ну я і написав. Залишається 2 хвилини до deadline'a - отримую відповідь, що asanakoy не проти об'єднатися, якщо я готовий і далі щось робити. Артем Санакоєв виявився PhD student in Computer Vision з вже наявними перемогами в змаганнях, що, звичайно не могло мене не радувати. Об'єднання в команди - величезний плюс участі в змаганнях на kag^, тому що це дозволяє новачкам на зразок мене дізнаватися щось нове від своїх більш досвідчених колег безпосередньо під час спільного вирішення завдання. Пробуйте брати участь, і якщо у вас будуть хоч невеликі успіхи і велике бажання щось робити, то вас обов'язково візьмуть в команду, де всьому навчать і все покажуть. Ну що ж, перше, що хочеться після об'єднання - отримати моментальний буст, об'єднавши свої рішення. Що ми і зробили, піднявшись при цьому на 30 рядків, що було цілком непогано, враховуючи що для цього було докладено мінімум зусиль. Завдяки Артему я зрозумів, що сильно недообучал свої мережі, і одна його модель в мотлох приділяла за якістю всі мої разом узяті. Але був ще час, щоб за допомогою порад свого співкомандника все виправити.

Ансамбль 2

І ось залишалося кілька днів до кінця змагання, Артем поїхав на конференцію CVPR, а я залишився сидіти з пачкою передбачень різних сіток. Усереднення - це звичайно добре, але існують більш просунуті техніки, такі як стекінг. Ідея наступна - розбити тренувальний сет, наприклад, на 5 частин, навчати модель на 4 з них, передбачати на 5-й,

і зробити так для всіх 5ти фолдів. Пояснююча картинка:

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

Як це застосувати до нашого завдання: є у нас передбачення ймовірностей кожної мережі на кожен з 17 класів, разом 6 мереж * 17 класів = 102 визнання. Це і буде нашою новою тренувальною вибіркою. На отриманій таблиці я навчав бінарний класифікатор для кожного з класів (разом 17 класифікаторів). В результаті на виході були ймовірності лейблів для кожного знімка. Далі до них можна застосувати вже використовуваний раніше жадібний алгоритм. Але, на мій подив, стекінг давав результати гірше, ніж просте усереднення. Для себе я це пояснив тим, що у нас були різні розбиття тренувального сету на фолди - Артем використовував 5 фолдів, а я тільки 4 (чим більше фолдів, тим зазвичай краще, але потрібно витрачати більше часу на навчання моделей). Тоді було вирішено робити склінг тільки на передбаченнях своїх нейромереж, а потім взяти зважену суму результату з передбаченнями Артема. В якості моделей другого рівня використовувалися: lightgbm, xgboost і тришаровий персептрон, після чого їх виходи усереднювалися.

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

Підсумки

Тим не менш, в результаті ми опинилися на 17 місці з майже тисячі, що для першого змагання з deep learning здається дуже непогано. Золоті медальки починалися з 11 місця, і я зрозумів, що ми були реально близькі, а рішення відрізняється від топового, можливо, тільки деталями реалізації.

Що ще можна було зробити

  1. На форумі багато писали, що архітектура Densenet показує дуже хороші результати, але через мою криворукість нестачі досвіду, не вийшло її підключити
  2. Зробити єдині фолди, та побільше (в чаті писали, що роблять по 10 фолдів)
  3. Для передбачення погоди можна було використовувати не одну модель, а кілька

На закінчення, хочеться подякувати чуйному ком'юніті чату ods.ai, де завжди можна запитати поради, і майже завжди допоможуть. Крім того, двом командам з чату вдалося зайняти 3 і 7 місця відповідно.