Quantcast
Channel: Найцікавіше на DOU
Viewing all articles
Browse latest Browse all 8115

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

$
0
0

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

Продовжуємо знайомитися з основами машинного навчання разом з веселими героями. Якщо ви ще не читали першу статтю «Вступ до 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. Складна задача: додати до моделі вхідний сигнал — статистика тестів і екзаменів з попередніх років.

Viewing all articles
Browse latest Browse all 8115

Trending Articles