Вступ до Machine Learning: перше знайомство з моделями

Цю статтю створен у співавторстві з Анастасією Білоус .

Машинне навчання і штучний інтелект за останні кілька років стали дуже гарячими темами. В тих чи інших варіантах вони сьогодні є частиною величезної кількості продуктів, і мало хто не задумується над їхнім запровадженням. Приклади застосування ML (Machine Learning) — від автоматичного визначення важливих листів і швидких відповідей Gmail, створення музики за допомогою машинного навчання до AlphaGo . Ця стаття також буде прочитана роботами швидше і більше разів, ніж людьми :)

У цьому матеріалі ми сфокусуємося виключно на темі машинного навчання і спробуємо на інтуїтивному рівні описати принципи його роботи. В Інтернеті є кілька визначень машинного навчання різного ступеню формальності, але в кінці статті ми прийдемо до свого власного. Оскільки це наш перший матеріал про машинне навчання на DOU, ми почнемо нашу, можна сказати екскурсію, з основ.

Знайте рішення

Візьмемо за приклад таку історію. Всесвітньо відомий український політехнічний університет (скорочено ПТУ) запровадив платну перездачу екзаменів. І у фінансового директора університету Іван Івановича виникла проблема: обсяги цих надходжень дуже складно запланувати наперед. Складніше ніж погоду, яка і так корелює з успішністю студентів. А Ryanair і all inclusive треба ж бронювати наперед.

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

def predict_num_failed_exams():
 result = 0
 for student in self.all_students:
 for exam in student.exams:
 if self.predict_exam_failure(student, exam):
 result = result + 1
 return result

Він задумався над реалізацією методу predictExamFailure(). Очевидно, що реалізація має брати до уваги попередні оцінки студентів, але як саме виразити це в коді — він не знав.

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

Рік Ім'я студента Предмет Балі тест 1 Балі тест 2 Екзамен*
2017 Андрій ОМ 47 53 52
2017 Аня ОМ 75 78 80
2017 Борис ОМ 52 47 47
2017 Марія ОМ 52 49 49
...

* Для здачі екзамену необхідно набрати мінімум 51 бал

Розглянувши результати, Коля повернувся до коду:

def predict_exam_failure(student, exam):
 return student[exam.subject].test1_grade < 51 or student[exam.subject].test2_grade < 51

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

return (student[exam.subject].test1_grade + student[exam.subject].test2_grade) < 100

Тепер прогноз для Андрея правильний, але виходить для Марії ми передбачимо тепер успішну складання екзамену. Хоча Коля і так знав, що Марічка завалила екзамен через нього, він вирішив, що результат першого тесту важливіший, ніж іншого, і знову переписавши свою формулу:

return (0.6 * student[exam.subject].test1_grade + 1.4 * student[exam.subject].test2_grade) < 100

І щоб вже вкотре переконатися у своїй геніальності, він вирішив перевірити, чи зможе його код точно передбачити результати минулих екзаменів, для яких результати вже були відомі. Він швидко написавши код, який рахує, у скількох відсотків випадків його передбачення збігається з фактичним результатом. Цю метрику називають точність (англ. accuracy).

def calculate_accuracy(students):
 num_correct = 0
 num_total = 0
 for student in students:
 for exam in student.exams:
 num_total += 1
 if predict_exam_failure(student, exam) == exam.final_grade > 50:
 num_correct += 1
 return 100.0 * num_correct/num_total

Точність

Запустивши цей метод на результати екзаменів минулого літа, він отримав точність своєї функції — 54,8%. Тут наш герой почав усвідомлювати, що його халявна курсова суттєво ускладнилася. Іван Іванович навряд чи зрозуміє, як же це він полетить у Туреччину з імовірністю 55%.

Коля витратив дуже багато часу, підбираючи різні варіанти коефіцієнтів у своїй функції, тестуючи її кожен раз на точність. Десь під ранок у нього була функція, точність якої сягала 75%! Самооцінка Колі піднялася до такого ж рівня. Альо перед тім як святкувати, він вирішив перевірити точність функції також на студентів інших факультетів. Після чого його самооцінка опустилася до 45%. Очевидно, що він так довго працював над своєю функцію, що «витиснув» з неї максимальну точність на іспитах свого факультету, альо це зробило її ще гіршою для інших. Цю ситуацію називають надмірне навчанняабо перенавчання (анг. overfitting).

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

Зобразивши результати екзамену крапками різних кольорів, Коля отримав такий малюнок:

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

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

Функцію, яку машина буде писати під конкретну задачу, будемо називати модель.

Моделі

Оскільки функція (модель) передбачає провал на екзамені, результат Правда (Істина) означає, що ми передбачаємо провал, і навпаки, Неправда (False) означає, що вона передбачає успішно будівель екзамен. Надалі ми абстрагуємося від екзаменів і будемо називати запису відповіді позитивнимиi негативними. При застосуванні та оцінювані моделі ми будемо пам " ятати, що означає «позитивний» в нашому випадку. Метрика точність (accuracy) не залежить від того, що ми називаємо позитивним, а що — негативним, тому що ми рахуємо кількість правильних передбачень.

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

Розглянємо наступний приклад, для простоти запису якого приймемо Х1 = балі за перший тест, Х2 = балі за другий тест. Наша функція виглядатиме так: F(X1, X2: number => Y: boolean — приймаємо два чисельні аргументи і повертаємо булевий результат. Цю функцію вище ми називали predict_exam_failure().

Для прикладу розглянємо одну з попередніх реалізацій нашої функції:

F(X1, X2) => 0.6 * X1 + 1.4 * X2 < 100

Виділені константи та 0.6 1.4 наша машина і буде підбирати. Ми будемо називати їх параметрамимоделі і позначатимемо W1 і W2, а Х1 і X2 — це властивостіданих. Y — вихідна властивість моделі. Модель зазвичай має більше, ніж дві вхідні властивості, узагальнений запис функції-моделі виглядає так: F(X1, X2, ... Xn) => Y. Звернемо увагу, що реалізація цієї функції з нашого прикладу матиме N параметрів — W1, W2, ... Wn. Такі моделі називають лінійними. Є багато інших способів зображати моделі, які ми опишемо в наступній статті, якщо буде до них інтерес :)

Для того, щоб оцінювати точність функції, ми додамо модель як вхідний параметр (не плутати з параметрами моделі) у наш метод для обрахунку точності, а вхідний параметр students перейменуємо в data для універсальності:

def calculate_accuracy(students, model_func):

Реалізація її залишається такою ж, як вище.

Тренування моделі

У найпростішому варіанті машинного навчання ми будемо використовувати код, який перебере багато різних параметрів і поверне тієї набір, для яких наша функція демонструє найбільшу точність. Цей процес називають тренуванням (training)моделі. Алгоритм тренування відповідно вчителемчи тренером (trainer). Цей код реалізовує тренера для нашого методу повного перебору:

def brute_force_train(data, min_w, max_w, train_step):
 best_model = None
 best_accuracy = 0
 w1 = min_w
 while w1 <= max_w:
 w2 = min_w
 while w2 <= max_w:
 model = lambda datum: w1 * datum[0] + w2 * datum[1]
 accuracy = calculate_accuracy(data, model)
 if accuracy > best_accuracy:
 best_model = model; best_accuracy = accuracy
 w2 += train_step
 w1 += train_step
 return best_model

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

Параметри min_w, max_w, train_step функції brute_force_train називаються гіперпараметрами (hyperparameters). Набір параметрів залежить від тренера (алгоритмом навчання), і їх вказують ті, хто тренує модель. На противагу параметрів W, які власне тренер сам і підбирає.

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

Спосібі боротьби з перенавчанням залежать від алгоритму і полягають у правильних значеннях гіперпараметрів тренера. На практиці оцінка моделі не проводитись на тих же вхідних даних, які використовувалися для тренування моделі. 10-20% усіх доступних даних відділимо в окремий набір і назвемо набором для оцінки. Інших 10-20% ми винесемо в набір для ратифікації, а 60-80%, які залишаться, «віддамо» тренеру. За яким принципом розділяти дані — залежить від даних і задачі. Випадкова вибірка часто є хорошим методом, якщо вхідні запису незалежні один від одного і немає сильного дисбалансу між кількістю позитивних і негативних записів.

Інтуїтивно аналогія тут така ж, як і з навчанням в університеті: викладач розв'язків язує одні задачі зі студентами на парах, а інші, але схожі задачі, дає на екзамені. Тут важливим є (і в навчанні студентів, і моделей) ті, що ці різноманітні задачі, i студенти не можуть просто є ятати відповіді, а ті, хто засвоїли матеріал (схожі задачі) зможуть повторити хід думок і відповісти правильно.

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

Призначення набору для ратифікацї ми також розберемо в наступній статті.

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

Розміри наборів для оцінки (і репрезентативність даних у них) є важливими для статистичної значущості показників, які рахуються на їх основі. Вище ми використовували точність (accuracy) як спосіб оцінки нашої моделі, і для даної проблеми ця метрика підходить. Альо давайте чи допустима, що позитивні записи є рідкістю, наприклад, модель передбачає ймовірність рідкісної хвороби і тільки 0,1% усіх записів є позитивними. У такому випадку модель, яка всі записи класифікує як негативні незалежно від їхніх властивостей, матиме точність 99,9% — неймовірну високу для будь-якого способу прийняття рішень. Але, як ми розуміємо, користь такої моделі дуже обмежена :)

Висновки

Тепер ми вже можемо дати і визначення.

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

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

Опубліковано: 16/05/18 @ 10:00
Розділ Різне

Рекомендуємо:

DOU Books: 5 класичних книг від Сергія Сыроватченко, SQL Server DBA
Senior Data Scientist із Лондона – про ринок Британії та майбутнє великих даних
SQL Server дайджест #15: другий сервіс пак для SQL Server 2016, як підняти сіквел на Маці і SQLSaturday Kyiv 2018
Геймдев: які є спеціалізації програмістів і з чого починати
Як скоро ваше місце займе AI