Штучний інтелект, виклики та ризики – очима інженера

Добрий день, колеги. Сьогодні хочеться тверезо подивитися очима інженера на так популярні зараз штучний інтелект і Deep learning, упорядкувати, вибудувати факти і виробити виграшну стратегію – як з цим… злетіти, пролетіти та не впасти комусь на голову? Тому що, коли справа від лабораторних моделей на python/matplotlib/numpy або lua доходить до высоконагруженного production в клієнтському сервісі, коли помилка у вихідних даних зводить нанівець усі зусилля – стає не те, що весело, а навіть починається нумерологічний середньовічний екстаз і інженери починають добу безперервно танцювати в надії вилікуватися від новомодної чуми )


Танцюючі інженери, марно сподіваються зцілитися

Сучасна розробка
Корисно для початку згадати, як в принципі влаштована сучасна розробка програмного забезпечення. За основу, як правило, беруться класичні, добре вивчені в «минулому столітті» алгоритми: пошук, сортування і т. д і т. п. Улюблені всіма СУБД – хороший приклад бородатих, ретельно перевірених і досліджених класичних алгоритмів (нехай пробачать нас Кодд і Дейт за таку попсу).

До алгоритмів додаються узгоджені співтовариством інженерів (хоча нерідко ні з ким не узгоджені, просунуті злою силою) – стандарти: мережа (DNS, TCP/IP), формати файлів, сервіси операційної системи (POSIX) і т. п.

Ну і, звичайно, мови. В останні роки, на жаль, у цій області почалася якась сумовита дурниці: додають-прибирають автоматичні геттери і сетери і автоматично вводять-виводять типи (як ніби це прямо так важливо важливо), розмазують ідеї функціонального програмування з «Аліси в Країні чудес» і Haskel на Scala, намагаються частково приховати дірки С++ у Rust, створюють C для гуманітаріїв у вигляді Go, в черговий раз придумують javascript «з нуля» (ECMAScript 6) і всім серцем продовжують вірити в красу моделі акторів і забивають абсолютно різні цвяхи з допомогою Erlang. Вся ця двіжуха, не без підстави, підігрівається ідеєю майбутнього багатопоточного програмування на багатоядерних процесорах і GPU, можливо в кластерах, з допомогою акторів, immutable-даних, функціонального ЗСЖ і неявних збочень у вигляді currying і рекурсивної побудови бінарного дерева. Але, очевидно, близького прориву поки ну зовсім не видно – логіка зіткнулася з фізикою і все стало впиратися в обмеження людського мозку, тупість компіляторів у вирішенні більш-менш цікавих завдань і здатність людей, ну вибачте, помилятися і без злого умислу плодити тонни багів. І все з новою силою звучить вислів Эдсгера Дейкстри, що серйозне програмування – для розумних, як не крути і ніякі чудо-юдо технології тут не допоможуть. Хоча може і правда нам допоможуть футуристичні квантові комп'ютери, ну треба ж у щось вірити?

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


Світ очима інженера

Що стосується бібліотек, то ніхто, зрозуміло, не пише зараз все з «нуля». Це нерозумно і дорого. Але, використовуючи «чужі» бібліотеки завжди залишається ризик, що їх писали трохи «неправильно», а вплинути на це в даний момент майже ніяк не можна, а зверху ще й підливають масла: «бери готове, ось вони взяли і у них працює». Так все, звичайно, і роблять і використовують Linux (операційка, але, по суті, така ж «бібліотека» доступу до «заліза»), nginx, apache, mysql, php, стандартні бібліотеки колекцій (java, c++ STL). Бібліотеки, на жаль, сильно відрізняються один від одного, як по швидкості, так і по ступені документованості і числа «неисправленных помилок» — тому успіху досягають команди, що володіють певним чуттям і відрізняють надійні «щурячі шкурки» від добре пропіареною, але мало корисних непрозорих та/або неадекватних при трохи нестандартної навантаженні рішень.

Таким чином, теоретично і, приклавши певні зусилля, практично можна створити адекватне програмне забезпечення в стислі строки з дуже обмеженими ресурсами, використовуючи математично перевірені алгоритми та бібліотеки достатньою, але не критичною ступеня зіпсованості, на доказавшем свою «нормальність» мовою/ах програмування. Прикладів успіху – не те, щоб багато, але вони є ;-)


Прикладів успіхів – не те, щоб багато, але вони є

Машинне навчання
У цій області, в основному, мова йде про учнів алгоритмах. Коли бізнес, скажімо, накопичив певну «бигдату» і хоче її монетизувати і витягнути корисний, допомагає клієнтам та/або підвищує продуктивність праці алгоритм. Аналітично в лоб завдання цю буває вкрай складно або неможливо вирішити, потрібні эксперто-роки, облік безлічі факторів, виклик Ктулху – і, здається, що можна поступити простіше і «нахабніше»: протягнути глибоку нейронну мережу «мордою» за даними і тикати її в них то тих пір, поки помилка стане нижче певного рівня (пам'ятаємо, що зазвичай перевіряють два типи помилок – помилка навчання і помилка генералізації на тестовому наборі даних). Ходить повір'я, що при наявності мільйона і більше прикладів – глибока нейронка здатна почати працювати на рівні не гірше людини, а якщо більше прикладів – є надія навчити її перевершувати людини. А якщо прикладів менше – нейронка теж може приносити свою посильну користь, допомагаючи, але не замінюючи людини.


Корисна нейронка всередині R2D2

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

Поріг входу в розробку і машинне навчання/Deep learning
Ні для кого не секрет, що технології розробки поділяються на категорії за рівнем входження. В найбільш простих і доступних технологіях людей завжди значно більше, часто з непрофільною освітою і в такій обстановці нерідко створюється багато неправильних, недовго живуть і воюють один з одним бібліотек – в цю нішу добре лягати JavaScript Node.js. Зрозуміло, що якщо копати вглиб, то з'являється безліч неочевидних деталей, з'являються справжні Гуру з великої літери – але увійти в цю область в шортах і сачком для метеликів за вихідні цілком реально.


Юні фронт-енд розробники на JavaScript. Але щоб стати мегагуру, доведеться довго вчитися, літа точно не вистачить.

В «середню» за рівнем входження категорію можна віднести динамічні мови програмування типу: PHP, Python, Ruby, Lua. Тут все вже набагато складніше – більше розвинені концепції ООП, частково реалізовані можливості функціонального програмування, іноді доступна примітивна багатопоточність, більш виражена модульність і засоби створення великих програм, доступні системні функції, частково реалізовані стандартні типи даних та алгоритми. За тиждень, без напруги, розібратися і почати створювати корисний код – цілком реально, навіть без профільної освіти.


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

У «вищу» за рівнем входження категорію зазвичай відносять добре зарекомендували себе промислові мови типу C++, Java, C# і вже начебто почали наступати їм на п'яти Scala, bash і VisualBasic (останні два — жарт). Тут вже ми зустрінемо дуже розвинені промислові інструменти управління складністю, якісні бібліотеки типових структур даних і алгоритмів, потужні можливості по створенню доменних діалектів, величезну кількість якісної документації, додаткові розвинені бібліотеки для налагодження, профілювання та чудові візуальні середовища розробки. Увійти і працювати в цій категорії найпростіше, маючи профільну освіту або велику любов до програмування і кілька років інтенсивного досвіду і хороше знання алгоритмів і структур даних – т.к. робота ведеться нерідко на досить низькому рівні і знання тонкощів операційної системи, мережевих протоколів – часто тут важливі.


Промислові мови програмування вимагають виснажливої підготовки і нерідко не прощають помилок

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

А ось з машинним навчанням трохи… інакше. Аналітиками часто просто народжуються. Процес навчання нагадує вивчення гри на музичному інструменті – 2-3 роки сольфеджіо, 2-3 роки гами ганяти до посиніння, 3 роки в оркестрі, 5 років різати трупи в морзі і тонни поту. Навчити людину азам математики і статистики, математичного аналізу, лінійної алгебри, диференціального числення, теорії ймовірностей – за місяці просто неможливо, потрібні роки і… і не все потягнуть і пройдуть на наступний курс. Багато зійдуть з дистанції на інші кафедри. Бути вченим – не для всіх, як би не хотілося.


Стажування аналітиків

А може, пронесе? Всі в це вірять по початку: за вихідні розберуся! Але на жаль, для того, щоб зрозуміти, як працює сама елементарна в машинному навчанні «логістична регресія», що є свого роду «hello world» в програмуванні, потрібно добре розуміти як мінімум пару розділів вищої математики: теорію ймовірностей і лінійну алгебру. А для розуміння логіки «стохастичного градієнтного спуску» потрібно також знати хоча б у базовому вигляді диференціальне числення.

Сирі підлозі-лабораторні фреймворки
Драйву додає висока вологість. Наявні на ринку популярні фреймворки для Deep learning – сирі до такої міри, що вранці на клавіатурі з'являється справжня цвіль.


Популярні фреймворки для машинного навчання. Вони не пропали, вони просто ще дуже сирі

Зрозуміло чому. Парад «універсальних» фреймворків почався лише в минулому, 2015 році. Deep learning впевнено пішов вгору в третій раз тільки в 2006 році, після багатьох десятиліть невпевненості і застою. GPU зовсім недавно раптово опинилися в потрібний час у потрібному місці.

На жаль, TensorFlow зовсім ще повільний і дивакуватий у production, Torch7 страждає відсутністю нормальної документації і мовою Lua, deeplearning4j намагається сподобається GPU, а кандидати типу Theano на python незрозуміло як ефективно експлуатувати у production без важких наркотиків. Так, ходять легенди, що навчання нейронної мережі це одне, а її експлуатація це сооовсем інше і цим повинні займатися зовсім інші люди і технології – але реальність вважає гроші і це, погодьтеся, дуже незручно, дорого і не дуже розумно. Найбільш універсальним і націленим на вирішення конкретних бізнес-завдань у стислі терміни на «нормальному» промисловому мовою є схоже поки тільки deeplearning4j – але теж знаходиться в фазі активного росту і дозрівання з усіма витікаючими.

Як вибрати архітектуру нейронної мережі для вирішення бізнес-завдання?
Читати наукові публікації на пташиному діалекті з відвертим матаном без мата можуть одиниці, тому для більшості інженерів швидше за все найбільш прикладної і корисний спосіб вивчення можливостей архітектури – це копирсання в исходниках фреймворків, у більшості випадком на «проклятому» студентському python і вивчення численних примерчиков, яких у різних фреймворках стає все більше і більше і це не може не радувати.

Тому загальний рецепт – виберіть найбільш підходящу розв'язуваної задачі архітектуру у вигляді прикладу в коді, реалізуйте її 1 в 1 в зручному для експлуатації фреймворку, замовте молебень і може вам пощастить! Що значить «може пощастить»? А все дуже просто. Ви зіткнетеся з наступним спектром інженерних ризиків:


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

1) Архітектура нейронки добре працює з даними дослідника, але з вашими даними може працювати зовсім «інше» або працювати зовсім навпаки.
2) У вашому фреймворку може не виявитися всього спектра елементарних кубиків: автоматичного диференціювання, алгоритму оновлення з тонкою підстроюванням (updater), розширених засобів регуляризації (dropout та інших), потрібної функції помилки (loss), певної операції над даними (векторне твір) і т. п. Ви можете замінити їх аналогами, але це привнесе ризики.
3) Іноді, правда рідко, хочеться потягнути production Matlab або R ;-) Тут одна порада – відразу до лікаря.
4) Швидше за все, вам буде потрібно підлаштувати нейронну мережу під додаткові бізнес-вимоги, які з'явилися пізніше і зовсім не вкладаються в ідеальний світ математики. Наприклад – значно знизити рівень хибно-позитивних спрацьовувань, збільшити Recall, знизити час навчання, адаптувати модель до набагато більшого набору даних, додати і враховувати нову інформацію. І тут, як правило, потрібно дуже глибоко вникати в архітектуру нейронки і крутити приховані гвинтики – а крутити на удачу, це шлях до зриву термінів релізу і нічним кошмарам. І без професора можна просидіти з викруткою з розгубленим виразом обличчя і місяць, і два і півроку.


Що ж робити, що крутити? Розробник на C++ намагається зрозуміти відмінність softmax softsign

Привіт тензори!
Для інженера тензор – це просто багатовимірний масив, що дозволяє здійснювати над собою різні операції і жертвопринесення. Але до роботи з тензорами – потрібно звикати ;-) Перші тижні навіть від тривимірних тензорів голова болить, не кажучи вже про набагато більш «глибоких» тензорах для рекурентних та згорткових мереж. Можна вбити купу часу на ці низькорівневі маніпуляції, налагодження циферок в тензорах і пошук помилок в одному значенні з 40 000. Обов'язково враховуйте цей ризик – тензори тільки здаються простими.


Будьте обережні! Спроби представити структуру 4 і більше мірних тензорів призводить до агресивного косоокості

Особливості роботи з GPU
Може бути на початку неочевидно, але зазвичай швидше навчати нейронку і отримувати її відповіді тоді, коли всі необхідні дані (тензори) завантажені в пам'ять GPU. А пам'ять цих дорогоцінних і так обожнюваних геймерами пристроїв – обмежена і зазвичай набагато менше пам'яті сервера. Доводиться городити милиці – вводити проміжне кешування тензорів в ОЗП, часткову генерацію тензорів під час проходження по набору даних (бо всі можуть і не поміститися) і так далі. Тому враховуємо і цей важливий інженерний ризик, що впливає на трудомісткість. Терміни можна сміливо множити на 3.


Відеокарта. На ній, виявляється, можна не тільки грати!

Нейронна мережа в production
Припустимо, вам сильно пощастило, ви добре попрацювали і довели лабораторний прототип до production якості, підняли веб-сервер, завантажуєте навчену нейронку в пам'ять сервера або відразу в пам'ять GPU і адекватно швидко віддаєте відповідь. Але… дані змінюються і за ними потрібно міняти/донавчати модель. Вам необхідно постійно стежити за якістю роботи нейронки, вимірювати її точність і ряд інших параметрів, що залежать від конкретної бізнес-завдання і ретельно продумати процедуру її оновлення та до/переучування. Повірте, мороки тут значно більше, ніж з класичної СУБД, яку потрібно лише раз на 5 років оптимізувати і раз в 10 років прибирати павутину з материнської плати :-)

А ще ходять легенди, що нейронну мережу можна просто… дообучить і не потрібно буде заново переучувати на всьому обсязі даних. Насправді — можна, але всім дуже дуже потрібно і іноді… щастить. Якщо даних досить мало і треба постаратися запам'ятати якомога більше (не знижуючи помилку на тестовому наборі даних, зрозуміло) – то просто так «дообучить» не «перевчаючи» без ризику щось важливе забути вже не вийде. Немає ніякої гарантії, що стохастичний градієнтний спуск (SGD) запам'ятавши нове, не забуде важливе старе ;-) А якщо багато (мільйони фотографій наприклад) і вимог запам'ятати саме цей конкретний приклад немає — спрацює (але молебень не завадить).

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

1) У вас все стало погано і неточно, тому що просто взяла і змінилася до біса інформація, прихована у вихідному наборі даних
2) У вас помилка в архітектуру нейронної мережі, і вона виявилася тільки що з-за зміни вихідних даних. Будьте ласкаві, надягайте каску і вивчайте градієнти і ваги кожного шару: немає загасання градієнта, немає «вибуху» градієнта, наскільки рівномірно розподіляються ваги і не потрібно підкрутити регуляризацию, немає проблем у функції помилки на виході (loss), немає згасання потоку інформації/градієнта в прикордонних режимах сигмоидов та інших специфічних функціях активації і т. д. і т. п. – головний біль забезпечена надовго і всерйоз і каска тільки для краси.
3) Вам пощастило і ви знайшли помилку в фреймворку нейронної мережі… за тиждень до релізу.

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


Настоянка з параної, витримка – 5 років

Висновки
Ми відкрито і чесно визначили ключові факти та ризики, пов'язані з впровадженням та використанням глибоких нейронних мереж в високонавантажених сервісах – з точки зору інженера. Висновки – поки що не робимо. Видно, що роботи багато, робота ця не проста, але страшно цікава і успіх приходить тільки до професіоналів, які вміють поєднувати знання і людей з різних областей і володіють смаком до синергії. Очевидно, що потрібно не просто чудово програмувати і відчувати систему кінчиками пальців, але і розуміти, або активно залучати до подібних проектів експертів в області математики і створювати колегам позитивні, творчі умови – щоб приходило побільше цікавих і ефективних ідей і способів їх лаконічною і швидкої реалізації. А інакше… доведеться місяцями сидіти з викруткою перед заваленим «Гравицапом», крутити навмання гвинтики, палити сірники і з заздрістю дивитися на блискучі в небі своєю міццю та красою штучного інтелекту рішення конкурентів. Бажаю всім інженерної удачі, сходиться нейронних мереж, впевненості, енергії і як можна менше важковловимих помилок! Ку! ;-)


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.