×Закрыть

Вступ до Machine Learning: створюємо першу модель

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

Продовжуємо знайомитися з основами машинного навчання разом з веселими героями. Якщо ви ще не читали першу статтю «Вступ до Machine Learning: знайомство з моделями» — зазирніть спочатку в неї. У цій статті ми перейдемо до практики роботи з TensorFlow.


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

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

Знайомство з Colaboratory і TensorFlow

Вован зрадів, що він хоч чимось може похизуватися перед дівчиною, і дістав свій ноутбук. Як виявилося, він уже мав певний досвід з машинним навчанням і показав Колі Colaboratory і TensorFlow. Коля швидко зрозумів, як це може стати йому в пригоді, пообіцяв Вовану пиво і побіг додому.

На одному диханні він прочитав документацію Colaboratory і сів розбиратися з програмним інтерфейсом TensorFlow. Він зберіг результати тестування з попередніх років у CSV файл для того, щоб полегшити процес завантаження даних, і завантаживши його на Диск Гугл, отримав загальнодоступну адресу файлу: drive.google.com/...​QKnhBDYsf0OIkGysZxjadWeTv

В Colaboratory він його завантажив наступним блоком коду:

!mkdir drive
!wget 'https://drive.google.com/uc?id=1nlTTlDYQKnhBDYsf0OIkGysZxjadWeTv&export=download' -O drive/exams.csv

Вован порекомендував використовувати бібліотеку Pandas для завантаження вхідних даних. Коля не зрозумів чому, але вирішив прислухатися. Наступним блоком коду вiн завантажив файл у pandas.DataFrame та вивів 15 верхніх рядків:

exams = pd.read_csv('drive/exams.csv')
exams.head(15)  # Виведемо 15 верхніх рядків

Ці дані він розділив на три набори, відділивши 20% у набір для ратифікації і ще 20% у набір для оцінки (дивитися чому — у попередній статті), решта 60% буде використовуватися тренером для навчання моделі. Ось код для розділення даних:

def split_data(data):
  training = []
  evaluation = []
  validation = []
  for (index, row) in enumerate(data.values):
    ind = index % 10
    if ind < 6:
      training.append(row)
    elif ind < 8:
      evaluation.append(row)
    else:
      validation.append(row)
  return (pd.DataFrame(training, columns=data.columns), 
          pd.DataFrame(evaluation, columns=data.columns), 
          pd.DataFrame(validation, columns=data.columns))
      
training_data, evaluation_data, validation_data = split_data(exams)

Йому було сказано зробити «аналіз даних» перед тренуванням моделей, але як його робити, Коля уявляв лише приблизно. Єдине, що він зрозумів з пояснень, — що це більше мистецтво, ніж наука. І для того, щоб знайти хоч якусь музу, потрібно дотримуватися першого правила аналізу даних: «Намалювати багато графіків і довго їх роздивлятися». Малювати графіки сам Коля не хотів, йому не терпілося уже приступити до тренування моделей. Лінь спонукала його шукати готове рішення. Одним з найпростіших йому здалося Facets — Visualizations for ML datasets, тому що воно доволі просто інтегрувалося в Colaboratory Facets for Colab (можете пропустити зараз цей момент і повернутися до нього після прочитання статті).

Тепер малювати багато графіків виявилося дуже просто — достатньо викликати функції FacetsDive() і FacetsOverview():

FacetsDive(training_data)
FacetsOverview(training_data, evaluation_data)

Порозглядавши їх, Коля не знайшов нічого надзвичайного і перейшов до визначення моделі. Йому потрібна була функція, яка, прийнявши (name, subject, test1, test2), передбачить успіх на екзамені. Так як успіх чи провал екзамену — величина булева, набори даних потрібно розширити додатковою колонкою:

outcome_column = 'failed'

# True = завалений екзамен (<= 50 балів)
training_data[outcome_column] = training_data['exam'] <= 50 
evaluation_data[outcome_column] = evaluation_data['exam'] <= 50

Тепер можна побудувати TensorFlow класифікатор (Classifier в нашому випадку LinearClassifier).

train_input = tf.estimator.inputs.pandas_input_fn(
      x=training_data[input_columns], # Вхідні колонки.
      y=training_data[outcome_column], # Вихідна колонка - те що ми передбачаємо.
      batch_size=50,
      num_epochs=None,
      shuffle=False)

# Визначення вхідних колонок для моделі, тут ми вказуємо тип кожної колонки
input_column_defs = [
    tf.feature_column.categorical_column_with_hash_bucket('name', 20),
    tf.feature_column.categorical_column_with_hash_bucket('subject', 20),
    tf.feature_column.numeric_column('test1'),
    tf.feature_column.numeric_column('test2'),
  ]

# Будуємо лінійний класифікатор
classifier = tf.estimator.LinearClassifier(input_column_defs,
  n_classes=2,
  optimizer=tf.train.AdagradOptimizer(
      learning_rate=0.2,)
)

# Тренуємо модель
classifier.train(train_input, steps=500)

Ми скористалися функцією classifier.train() для того, щоб навчити класифікатор на наших вхідних даних. Після тренування ми можемо його використовувати. Нас цікавитимуть два основні методи — classifier.evaluate() і classifier.predict().

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

eval_input = tf.estimator.inputs.pandas_input_fn(
        x=evaluation_data[input_columns],
        y=evaluation_data[outcome_column], 
        num_epochs=1,
        shuffle=False)
classifier.evaluate(eval_input)

Результат:

{'accuracy': 0.92771083,
 'accuracy_baseline': 0.77710843,
 'auc': 0.98397243,
 'auc_precision_recall': 0.9539273,
 'average_loss': 0.17726704,
 'global_step': 500,
 'label/mean': 0.22289157,
 'loss': 14.713165,
 'precision': 1.0,
 'prediction/mean': 0.1676192,
 'recall': 0.6756757}

У першій статті ми ввели метрику точність (Accuracy). Як ми бачимо, тут для нашої моделі вона доволі висока — 93%. Тепер ми можемо написати функцію для передбачення успіху конкретного екзамену:

def predict_exam_failure(name, subject, test1, test2):
  # Пакуємо вхідні дані в DataFrame
  data = pd.DataFrame(data={'name': [name], 'subject': [subject], 'test1': [test1], 'test2': [test2]})

  data_input = tf.estimator.inputs.pandas_input_fn(
    x=data,
    shuffle=False)

  predictions = classifier.predict(data_input)
  the_prediction = next(predictions)  # Витягуємо єдиний запис - так як на вхід був один рядок
  return the_prediction['probabilities'][1] # probabilities буде мати два значення - ймовірність False і ймовірність True - в сумі вони мають давати 1.
  # Нас цікавить ймовірність True - провалу екзамену.

Фінальний результат можна побачити в Додатку Colaboratory.

Використовуючи цю функцію, Коля передбачив кількість провалів екзаменів на сесії і доповів результат Івану Івановичу. Той страшенно зрадів цифрі і пообіцяв Колі найвищу оцінку на захисті. Іван Іванович не очікував, що Марічка здогадається про його план і розголосить про цю аферу в університетському подкасті «Голос ПТУ». Щоб уникнути скандалу, Іван Іванович оголосив, що на виручені кошти він насправді хотів побудувати новий комп’ютерний клас і купити Cloud-ресурси для того, щоб Коля міг тренувати більше корисних моделей і вчити інших моторних парубків машинному навчанню.

Коля, не звертаючи увагу на скандал навколо його курсової, впевнено написав у себе в LinkedIn: «Треную і оцінюю моделі». Коли йому подзвонила Люська і запропонувала запросити її в ресторан (що вона вважала неймовірним кроком зі свого боку), Коля сказав, що він зайнятий. Без штучного інтелекту було зрозуміло що Люська йому не потрібна, тим більше, що він зацікавився проектом, який запропонувала йому Марічка. Вона була вражена успіхами в передбаченні того, хто завалить екзамен, і в неї виникла ідея організувати позакласну підготовку до екзаменів для тих, у кого великий ризик його завалити. Гуртки мали проходили у кафешці її тата, а Коля у разі успіху отримував туди вільний вхід.

Нові метрики для оцінки точності моделі

Розмірковуючи над проектом, Коля зрозумів, що метрика «Точність», яку він використовував для оцінки моделей з курсової, не підійде в цьому випадку через, те що більшість буде її демократично сильно викривляти. Наприклад, якщо тільки 10% екзаменів завершуються невдачею, модель, яка завжди повертає «успіх» як результат, буде мати точність 90%... І ця проблема буде тим актуальніша, чим більш розбалансований набір даних.

Якщо вхідні дані можуть належати до одного з двох класів (успіх чи провал екзамену), і наша модель передбачає один з цих класів, у результаті ми маємо 4 різні варіанти:

1) передбачений провал і фактичний провал;
2) передбачений успіх, але фактичний провал;
3) передбачений провал, але фактичний успіх;
4) передбачений успіху і фактичний успіх.

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

РезультатФактично ПозитивнийФактично Негативний
Передбачений Позитивний Коректно Позитивні = 118Некоректно Позитивні = 200
Передбачений НегативнийНекоректно Негативні = 60Коректно Негативні = 460

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

Введемо дві нові метрики для оцінки точності моделі:

Позитивна точність (Precision)

= Коректно Позитивні / (Коректно Позитивні + Некоректно Позитивні)
= Коректно Позитивні / Передбачені Позитивні

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

Приклад: якщо модель вибрала 20 екзаменів, які передбачила як провалені, скільки з них буде фактично проваленими.

Покриття (Recall)

= Коректно Позитивні / (Коректно Позитивні + Некоректно Негативні)
= Коректно Позитивні / Фактично Позитивні

Інтуїція метрики — процент вибраних моделю позитивних записів.

Приклад: якщо модель вибрала 20 екзаменів, які передбачила як провалені, який відсоток ця цифра складає від усіх фактично провалених.

Поріг

Повертаючись назад до функції predict_exam_failure(), вона повертала число від нуля до одиниці — ймовірність позитивного результату (провалу екзамену) для вхідного запису. Однією із задач Колі було визначити поріг для цієї ймовірності, який дає найкраще значення метрики. Це значення дозволить йому написати код на зразок (де 0.6 — наше значення порогу):

if predict_exam_failure(student.name, exam.subject, exam.text1, exam.test2) >= 0.6:
  num_failed += 1

Яким чином ми можемо отримати значення 0.6? Наприклад, побудувавши таблицю на зразок такої:

Значення порогуТочність
05%
0.242%
0.457%
0.693%
0.876%
111%

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

Метрики «позитивна точність» і «покриття» знаходяться в конфлікті одна з одною. Ми легко можемо отримати покриття 100%, просто передбачаючи позитивний результат для будь-якого запису даних (значення порогу = 0). Це призведе до низької точності — багато із записів, передбачених нами як позитивні, будуть фактично негативними.

Також легко отримати високу позитивну точність, вибравши дуже високе значення порогу, наприклад 0.95. Але таке велике значення призведе до низького покриття. Багато фактично позитивних записів не «дотягнуться» до такого високого порогу і будуть передбачені як негативні. Для правильного вибору порогу ми мусимо спочатку визначитися з правильною метрикою, яка є для нас важлива, а вибір метрики виходить з конкретної бізнес-проблеми, яку ми вирішуємо.

Практичні завдання

Для закріплення матеріалу пропонуємо охочим відповісти в коментарях на такі запитання і зробити практичні завдання (для практичних завдань необхiдно зробити копію Colaboratory, дописати туди необхідний код і додати посилання в коментарі):

  1. Чому рік не може бути вхідним сигналом?
  2. Якщо ми не маємо жодної інформації про екзамен — ні предмету, ні року, ні студента, ні результатів тесту, то з якою ймовірністю він буде успішно зданий? А якщо ми знаємо тільки ім’я студента — Андрій?
  3. Яким чином ми можемо порахувати значення точності для таблиці «Точність» для значень порогу?
  4. Що цікавого ви побачили в даних?
  5. Побудуйте графік (Точність, Позитивна Точність і Покриття) для різних значень порогу з набору даних для оцінки.
  6. Наведіть приклади задач, для яких раціональним є вибір низьких значень порогів? А коли високих?
  7. Натренувати модель, додавши до нього додаткові вхідні сигнали — стать студента.
  8. Наведіть приклади даних, яких немає в заданому наборі, але які б могли суттєво покращити метрики моделі.
  9. Складна задача: яким чином можна порахувати, наскільки наша модель перенавчилася? Напишіть код.
  10. Складна задача: додати до моделі вхідний сигнал — статистика тестів і екзаменів з попередніх років.
LinkedIn

21 комментарий

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Хороша, мотивуюча стаття, після якої легко почати працювати з TensorFlow з якимись мінімальними знаннями. Буду радий побачити продовження. Поки що спробую відповісти на самі легкі, на мій погляд, питання, інші залишу «на подумати». Я новачок в ML, тому можу допускати помилки в своїх відповідях. Буду вдячний за виправлення і коментарі.

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

2. Якщо відсутня будь-яка інформація, то ймовірність не може бути визначеною. Ім’я студента, на мій погляд, слабенька ознака, яка може дати дуже низьку точність і покаже тільки «середнє по Андріям».

3. Можна порахувати точність, роблячи передбачення на даних, які використовувалися для тренування моделі і порівняти кількість правильно передбачених результатів з фактичними.

4. Дуже сильний зв’язок між успішною здачею тестів і екзаменом. Помітно, що деякі предмети завалюють частіше, ніж інші. Також, якись підозріло великий вплив імені на результати екзамену. Як на мене, вони надто скупчені, наприклад у всіх Кирилів високі результати, а у Вір низькі :)

8. Хотілося би бачити ідентифікатор викладача, так як суб’єктивність в оцінюванні може покращити результати передбачення. Ще хотілося би бачити тип екзамену, так як, комусь може даватися легше усний екзамен, а комусь тести. Якщо заглиблюватися, то усний екзамен може оцінюватися більш суб’єктивно, ніж тести, тому можна було б поекспериментувати з додатковим параметром, який включатиме id викладача і тип екзамену.

Також хотів би додати, що FacetsDive і FacetsOverview відображаються тільки в Хромі. Я довго не міг зрозуміти, що я роблю не так, поки не натрапив на це github.com/...​er/README.md#known-issues

1. Так
2. А що «середнє по Андріях» це не ймовірність? підказка — відповідь на це питання має бути числом від 0 до 1, а точніше два числа:)
3. Кожен раз з різним порогом? А простіше? якщо я хочу побудувати графік на 1000 точок це буде трохи затратний спосіб особливо якщо набір даних для оцінки великий.
4. Все вірно.
8. Вірно, а ще?:)

Дякую за підказку на рахунок Facets і Chrome!

Спробую ще раз, правда не впевнений, що все вірно і цього разу :)
2. Якщо є інформація про відсоток незданих екзаменів за попередні роки, тоді це і буде ймовірністю його завалити. А якщо у нас є Андрії і не Андрії, то ймовірність завалити екзамен можна буде передбачити окремо вирахувавши відсоток завалених екзаменів для Анріїв і для всіх інших.
3. Можна один раз зробити передбачення на тренувальних даних і кудись його зберегти, наприклад, створити секцію в Jupiter Notebook з кодом
exams['prediction'] = exams.apply(lambda e: predict_exam_failure(e['name'], e['subject'], e['test1'], e['test2']), axis = 1)
Або записати фактичні результати і передбачені в csv файл і зберегти на диск. Можливо існує якись елегантіший спосіб це зробити, але поки що я ні до чого кращого не додумався.
8. В цьому випадку чим більше даних, тим краще, пізніше можна буде розібратися, які дані впливають на результати, а які ні :) З того, що приходить в голову, це класифікація предметів, наприклад технічні і гуманітарні (можна створити і більш розширену класифікацію), стать студента, вік студента, успішність за попередні роки (також в розрізі по класифікації предметів), середній бал по предмету, чи вирішальним являється результат екзамену для отримання стипендії, кількість перездач за попередні роки, відвідуваність предмету.

Добре! Спробуйте складніші запитання:)

2. У Вас є ця інформація:) див. посилання на дані ввгорі.
3. Напишіть код який порахує таблицю.
8. Непогані варіанти.

Классные статьи, пишите еще.
Качественного материала по ML на украинском (русском) очень мало.
Ответ по заданию напишу сразу после освоения основ ML и python :)

Гарна стаття, але на жаль друга частина це вже про «наше життя, а не про Machine Learning».

Хочу поділитися власним IMHO, бо стаття багато в чому суперечить підходам на сертифікації як в Гугл, так і у інших компаніях.

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

Маю певний досвід складання іспитів на сертифікацію в тому числі від Google, де Ви зараз працюєте відповідно до Вашого тайтлу.

Був дуже здивований коли побачив у статті:

що це більше мистецтво, ніж наука. І для того, щоб знайти хоч якусь музу, потрібно дотримуватися першого правила аналізу даних: «Намалювати багато графіків і довго їх роздивлятися». Малювати графіки сам Коля не хотів, йому не терпілося уже приступити до тренування моделей. Лінь спонукала його шукати готове рішення. Одним з найпростіших йому здалося Facets — Visualizations for ML datasets, тому що воно доволі просто інтегрувалося в Colaboratory Facets for Colab (можете пропустити зараз цей момент і повернутися до нього після прочитання статті).

Тепер малювати багато графіків виявилося дуже просто — достатньо викликати функції FacetsDive() і FacetsOverview()

і стаття перейшла з категорії «про Machine Learning» в категорію «про наше життя» :)

Ми маємо достатньо тривіальну задачу: спрогнозувати імовірність настання майбутньої події.

Студент маркетолог курсу третього вирішить таку задачу просто:
візьмемо показники виручки за попередні періоди і порахуємо динаміку. Це розв’язок на «трійку». До речі Іван Іванович так може зробити і сам. Дуже просто спитати скільки «бігунків» біло виписано і помножити на вартість «побору за перездачу».

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

Для кожного сегменту рахуємо коефіцієнт і маємо результат.

На «відмінно» додамо такі коригуючі коефіцієнти як:
рік навчання студенту
предмет
викладач
спеціальність
факультет
іспит чи залік
вид сесії «зимова» чи «літня»
навчання очне чи заочне
наявність у студента попередніх перездач
скільки перездач потрібно студенту на один предмет.

Зрозуміло, що такі коефіцієнти треба рахувати на попередніх періодах та дивитись динаміку.

От повертаючись до наших статей маємо:
некомпетентного керівника Івана Івановича (міг би і сам знати надходження за перездачі в попередні періоди) та некомпетентного виконавця, який «готовий на все» і боїться запитати про додаткові данні.

Тому і маємо виконання задачі на «колінках» з «того, що є».

Більше того, Ваш підхід у статті

що це більше мистецтво, ніж наука

взагалі не заслуговує навіть критики. Є конкретна ситуація. Для неї є відповідні моделі для аналізу вибачте, але після

першого правила аналізу даних: «Намалювати багато графіків і довго їх роздивлятися»

я просто захотів Вас спитати, «Ви дійно вважаєте такий підхід правильним???» Щось мені підказує що ми маємо як мінімум проблему з масштабуванням та ефективністю.

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

Більше того такі «Колі» і вчать потім інших таких «Коль», такому, що вони самі змогли зрозуміти «на свій хлопський розум».. Більше того, вчать «за їжу», бо кафешка навіть не Коліна

Гуртки мали проходили у кафешці її тата, а Коля у разі успіху отримував туди вільний вхід.

Так завжди, гешефт отримав хтось інший :(

Дякую у мене все :)

P.S. Не сприймайте за образу, якщо я не відповім на Ваш коментар. Вже майже рік не маю часу читати ДОУ, а цю статтю побачив випадково.

Дякую! В мене зараз проходить стажування з учнями по Python 1 року навчання (14-15 років). Запропоную у понеділок їм спробувати, але турбуюся за математику...

Буду вдячний якщо поділитеся досвідом як їм пішло

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

Напишу з радістю, але хотілося б почути питання по тому що не зрозуміло і відповіді на завдання у статті.

Немного залип на позитивной точности и покрытие. Возможно их лучше визуализировать картинкой или диаграмкой.

Допустимо прогноз погоди (з цілого тижня) передбачив дощ на 5 днів з понеділка по пятницю.
Дощ насправді випав в неділю, понеділок і вівторок.

Точність = 3 / 7 (три дні (пон, вів, суб) передбачені правильно, решта неправильно)
Позитивна точність (Precision) = 2 / 5 (позитивне передбачення справдилося для пон і вів)
Покриття (Recall) = 2 / 3 (з насправді позитивних нед-вів правильно вибраний пон і вів)

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

Если идти по тексту и заполнять свой нотбук, то теряется :
input_columns = [’name’, ’subject’, ’test1′, ’test2′]

Дякую! попрошу редакцію виправити.

Суперова стаття, дякую :)

А тут плакались, что нет на украинском. Кстати, статья для новичков в этом деле отличная.
Вот только не хватает картинок формируемых сетей, с ними было бы лучше.
И кстати украинский тут понятный, как для бульбаша. Хорошо пишешь.

Дякую! До картинок ще доберемося, наразі як на нашу думку трохи рано. Хотілося б побачити як читачі справляться з завданнями до статті

Вован порекомендував використовувати бібліотеку Pandas для завантаження вхідних даних. Коля не зрозумів чому, але вирішив прислухатися

Сделали мой вечер)))

Подписаться на комментарии