задачи по машинному обучению python
Машинное обучение на Python-е с интерактивными Jupyter демонстрациями
Недавно я запустил репозиторий Homemade Machine Learning, который содержит примеры популярных алгоритмов и подходов машинного обучения, таких как линейная регрессия, логистическая регрессия, метод K-средних и нейронная сеть (многослойный перцептрон). Каждый алгоритм содержит интерактивные демо-странички, запускаемые в Jupyter NBViewer-e или Binder-e. Таким образом у каждого желающего есть возможность изменить тренировочные данные, параметры обучения и сразу же увидеть результат обучения, визуализации и прогнозирования модели у себя в браузере без установки Jupyter-а локально.
Целью данного репозитория является реализация алгоритмов почти с нуля, для того, чтобы иметь более детальное понимание математических моделей, который стоят за каждым из алгоритмов. Основными используемыми библиотеками стали NumPy и Pandas. Эти библиотеки используются для эффективных операций над матрицами, а так же для загрузки и парсинга CSV данных. В демо-страничках для построения графиков и визуализации тренировочных данных так же используются библиотеки Matplotlib и Plotly. В случае с логистической регрессией для минимизации функции потерь используется библиотека SciPy, но в остальных случаях градиентный спуск реализован на чистом NumPy/Python-е. Использование библиотек на подобии PyTorch или TensorFlow избегается из-за обучающей цели репозитория.
На данный момент в репозитории реализованы следующие алгоритмы…
Регрессия. Линейная регрессия.
В задачах, связанных с регрессией мы пытаемся предсказать реальное число на основании входящих данных. По сути мы строим линию/плоскость/n-мерную плоскость вдоль тренировочных данных, чтобы иметь возможность сделать прогноз для входных данных, отсутствующих в тренировочном сете. Это происходит, например, если мы хотим предсказать стоимость 2-х комнатной квартиры, в центре города N, на 7-м этаже.
Классификация. Логистическая регрессия.
В задачах связанных с классификацией мы разбиваем данные на классы в зависимости от параметров этих данных. Примером задачей классификации может быть распознавание спама. В зависимости от текста письма (входящие данные) мы относим каждое письмо к одному из двух классов («спам» или «не спам»).
Кластеризация. Метод K-средних.
В задачах кластеризации мы разбиваем наши данные на кластеры, которые заранее нам неизвестны. Эти алгоритмы могут использоваться для сегментации рынка, анализа социальных и не только сетей.
Нейронные сети. Многослойный перцептрон (MLP).
Нейронные сети скорее являются не алгоритмом, а «паттерном» или «фреймворком» для организации разных алгоритмов машинного обучения в одну систему для дальнейшего анализа сложных входных данных.
Поиск аномалий с помощью распределения Гаусса
В задачах связанных с поиском аномалий мы пытаемся выделить те экземпляры данных, которые выглядят «подозрительно» в сравнении с большинством других экземпляров. Например определение нетипичных (подозрительных) транзакций по банковской карте.
Я надеюсь, что вы найдете репозиторий полезным, либо экспериментируя с демонстрациями каждого алгоритма, либо читая о математических моделях, стоящими за ними, либо анализируя детали имплементации каждого из них.
Делаем проект по машинному обучению на Python. Часть 3
Многим не нравится, что модели машинного обучения представляют собой чёрные ящики: мы кладём в них данные и безо всяких объяснений получаем ответы — часто очень точные ответы. В этой статье мы постараемся разобраться, как созданная нами модель делает прогнозы и что она может рассказать о решаемой нами задаче. И завершим мы обсуждением самой важной части проекта по машинному обучению: задокументируем сделанное и представим результаты.
В первой части мы рассмотрели очистку данных, разведочный анализ, конструирование и выбор признаков. Во второй части изучили заполнение отсутствующих данных, реализацию и сравнение моделей машинного обучения, гиперпараметрическую настройку с помощью случайного поиска с перекрёстной проверкой и, наконец, оценку получившейся модели.
Весь код проекта лежит на GitHub. А третья Jupyter Notebook, относящаяся к этой статье, лежит здесь. Можете её использовать для своих проектов!
Итак, мы работаем над решением задачи с помощью машинного обучения, точнее, с помощью управляемой регрессии (supervised regression). На основе данных об энергопотреблении зданий в Нью-Йорке мы создали модель, прогнозирующую количество баллов Energy Star Score. У нас получилась модель «регрессия на основе градиентного бустинга», способная на основе тестовых данных прогнозировать в пределах 9,1 пунктов (в диапазоне от 1 до 100).
Интерпретация модели
Регрессия на основе градиентного бустинга располагается примерно посередине шкалы интерпретируемости моделей: сама модель сложная, но состоит из сотен довольно простых деревьев решений. Есть три способа разобраться в работе нашей модели:
Важности признаков
Важности признаков позволяют увидеть связь каждого признака с целью прогнозирования. Технические подробности этого метода сложны (измеряется среднее уменьшение инородности (the mean decrease impurity) или уменьшение ошибки из-за включения признака), но мы можем использовать относительные значения, чтобы понять, какие признаки более релевантны. В Scikit-Learn можно извлечь важности признаков из любого ансамбля «учеников» на основе деревьев.
В приведённом ниже коде model — наша обученная модель, и с помощью model.feature_importances_ можно определить важности признаков. Затем мы отправим их в кадр данных Pandas и выведем на экран 10 самых важных признаков:
На основе этих результатов можно наконец-то ответить на один из исходных вопросов: самыми важными индикаторами количества баллов Energy Star Score являются Site EUI и Weather Normalized Site Electricity Intensity. Не будем слишком углубляться в дебри важностей признаков, скажем лишь, что с них можно начать разбираться в механизме прогнозирования моделью.
Визуализация одного дерева решений
С помощью визуализатора Graphviz преобразуем dot-файл в png, введя в командной строке:
Получили полное дерево решений:
Немного громоздко! Хотя это дерево глубиной всего в 6 слоёв, отследить все переходы трудно. Давайте изменим вызов функции export_graphviz и ограничим глубину дерева двумя слоями:
Каждый узел (прямоугольник) дерева содержит четыре строки:
(Листья содержат только 2.–4., потому что они представляют собой финальную оценку и не имеют дочерних узлов).
Формирование прогноза для заданного измерения в дереве решений начинается с верхнего узла — корня, а затем спускается вниз по дереву. В каждом узле нужно ответить на задаваемый вопрос «да» или «нет». Например, на предыдущей иллюстрации спрашивается: «Site EUI здания меньше или равно 68.95?» Если да, алгоритм переходит в правый дочерний узел, если нет, то в левый.
Во второй статье мы настроили количество гиперпараметров модели, которые управляют каждым деревом, например, максимальную глубину дерева и минимальное количество образцов, необходимых для каждого листа. Два этих параметра сильно влияют на баланс между пере- и недообучением, а визуализация дерева решений позволит нам понять, как работают эти настройки.
Хотя мы не сможем изучить все деревья в модели, анализ одного из них поможет разобраться в том, как прогнозирует каждый «ученик». Этот основанный на блок-схеме метод очень похож на то, как принимает решение человек. Ансамбли из деревьев решений комбинируют прогнозы многочисленных отдельных деревьев, что позволяет создавать более точные модели с меньшей вариативностью. Такие ансамбли очень точны и просты в объяснении.
Локальные интерпретируемые моделезависимые объяснения (LIME)
Последний инструмент, с помощью которого можно постараться разобраться в том, как «думает» наша модель. LIME позволяет объяснить, как сформирован одиночный прогноз любой модели машинного обучения. Для этого локально, рядом с каким-нибудь измерением на основе простой модели наподобие линейной регрессии создаётся упрощённая модель (подробности описаны в этой работе: https://arxiv.org/pdf/1602.04938.pdf).
Мы воспользуемся методом LIME, чтобы изучить совершенно ошибочный прогноз нашей модели и понять, почему она ошибается.
Сначала найдём этот неверный прогноз. Для этого обучим модель, сгенерируем прогноз и выберем значение с наибольшей ошибкой:
Prediction: 12.8615
Actual Value: 100.0000
Затем создадим объясняющий объект (explainer) и передадим ему обучающие данные, информацию о режиме, метки для обучающих данных и имена признаков. Теперь можно передать explainer’у данные наблюдений и функцию прогнозирования, а потом попросить объяснить причину ошибочности прогноза.
Диаграмма объяснения прогноза:
Как интерпретировать диаграмму: каждая запись по оси Y обозначает одно значение переменной, а красные и зелёные полосы отражают влияние этого значения на прогноз. Например, согласно верхней записи влияние Site EUI больше 95,90, в результате из прогноза вычитается около 40 пунктов. Согласно второй записи влияние Weather Normalized Site Electricity Intensity меньше 3,80, и поэтому к прогнозу прибавляется около 10 пунктов. Итоговый прогноз представляет собой сумму интерсепта и влияний каждого из перечисленных значений.
Слева показан процесс принятия решения моделью: визуально отображено влияние на прогноз каждой переменной. В таблице справа приведены фактические значения переменных для заданного измерения.
В данном случае модель спрогнозировала около 12 баллов, а на самом деле было 100. Поначалу вы можете недоумевать, почему так произошло, но если проанализировать объяснение, то оказывается, что это не крайне смелое предположение, а результат расчёта на основе конкретных значений. Значение Site EUI было относительно высоким и можно было ожидать низкого балла Energy Star Score (потому что на него сильно влияет EUI), что наша модель и сделала. Но в данном случае эта логика оказалась ошибочной, потому что по факту здание получило наивысший балл Energy Star Score — 100.
Ошибки модели могут вас расстраивать, но подобные объяснения помогут понять, почему модель ошиблась. Более того, благодаря объяснениям можно начать раскапывать, почему здание получило высший балл несмотря на высокое значение Site EUI. Возможно, мы узнаем что-то новое о нашей задаче, что ускользнуло бы от нашего внимания, не начни мы анализировать ошибки модели. Подобные инструменты не идеальны, но они могут сильно облегчить понимание работы модели и принимать более правильные решения.
Документирование работы и представление результатов
Во многих проектах уделяется мало внимания документации и отчётам. Можно сделать лучший анализ в мире, но если не представить результаты должным образом, они не будут иметь значения!
Документируя проект по анализу данных, мы упаковываем все версии данных и код, чтобы проект могли воспроизвести или собрать другие люди. Помните, что код читают чаще, чем пишут, поэтому наша работа должна быть понятна и другим людям, и нам, если мы вернёмся к ней через несколько месяцев. Поэтому вставляйте в код полезные комментарии и объясняйте свои решения. Блокноты Jupyter Notebook — прекрасный инструмент для документирования, они позволяют сначала объяснять решения, а потом показывать код.
Также Jupyter Notebook хорошая платформа для взаимодействия с другими специалистами. С помощью расширений для блокнотов можно скрыть код из финального отчёта, поскольку, как бы вам ни было трудно в это поверить, не все хотят видеть кучу кода в документе!
Возможно, вам захочется не делать выжимку, а показать все подробности. Однако важно понимать свою аудиторию, когда представляешь свой проект, и соответствующим образом составить отчёт. Вот пример краткого изложения сути нашего проекта:
В конечном счёте, ценность нашей работы определяется решениями, которые она помогает принять, и очень важно уметь «подать товар лицом». Правильно документируя, мы помогаем другим людям воспроизвести наши результаты и дать нам обратную связь, что позволит нам стать опытнее и в дальнейшем опираться на полученные результаты.
Выводы
В нашей серии публикаций мы разобрали от начала до конца учебный проект по машинному обучению. Начали с очистки данных, затем создали модель, и в конце научились её интерпретировать. Напомним общую структуру проекта по машинному обучению:
Открытый курс машинного обучения. Тема 2: Визуализация данных c Python
Второе занятие посвящено визуализации данных в Python. Сначала мы посмотрим на основные методы библиотек Seaborn и Plotly, затем поанализируем знакомый нам по первой статье набор данных по оттоку клиентов телеком-оператора и подглядим в n-мерное пространство с помощью алгоритма t-SNE. Есть и видеозапись лекции по мотивам этой статьи в рамках второго запуска открытого курса (сентябрь-ноябрь 2017).
UPD: теперь курс — на английском языке под брендом mlcourse.ai со статьями на Medium, а материалами — на Kaggle (Dataset) и на GitHub.
Сейчас статья уже будет существенно длиннее. Готовы? Поехали!
План этой статьи
Демонстрация основных методов Seaborn и Plotly
В начале как всегда настроим окружение: импортируем все необходимые библиотеки и немного настроим дефолтное отображение картинок.
После этого загрузим в DataFrame данные, с которыми будем работать. Для примеров я выбрала данные о продажах и оценках видео-игр из Kaggle Datasets.
Seaborn
Познакомимся с первым таким «сложным» типом графиков pair plot ( scatter plot matrix ). Эта визуализация поможет нам посмотреть на одной картинке, как связаны между собой различные признаки.
Как можно видеть, на диагонали матрицы графиков расположены гистограммы распределений признака. Остальные же графики — это обычные scatter plots для соответствующих пар признаков.
Для понимания лучше один раз увидеть, поэтому вот еще и картинка с Wikipedia:
Plotly
Прелесть интерактивных графиков заключается в том, что можно посмотреть точное численное значение при наведении мыши, скрыть неинтересные ряды в визуализации, приблизить определенный участок графика и т.д.
Для начала построим line plot с динамикой числа вышедших игр и их продаж по годам.
Параметр show_link отвечает за ссылки на online-платформу plot.ly на графиках. Поскольку обычно это функциональность не нужна, то я предпочитаю скрывать ее для предотвращения случайных нажатий.
Можно сразу сохранить график в виде html-файла.
С помощью plotly можно построить и другие типы визуализаций. Графики получаются достаточно симпатичными с дефолтными настройками. Однако библиотека позволяет и гибко настраивать различные параметры визуализации: цвета, шрифты, подписи, аннотации и многое другое.
Пример визуального анализа данных
Считываем в DataFrame знакомые нам по первой статье данные по оттоку клиентов телеком-оператора.
Проверим, все ли нормально считалось – посмотрим на первые 5 строк (метод head ).
Число строк (клиентов) и столбцов (признаков):
Посмотрим на признаки и убедимся, что пропусков ни в одном из них нет – везде по 3333 записи.
| Название | Описание | Тип |
|---|---|---|
| State | Буквенный код штата | категориальный |
| Account length | Как долго клиент обслуживается компанией | количественный |
| Area code | Префикс номера телефона | количественный |
| International plan | Международный роуминг (подключен/не подключен) | бинарный |
| Voice mail plan | Голосовая почта (подключена/не подключена) | бинарный |
| Number vmail messages | Количество голосовых сообщений | количественный |
| Total day minutes | Общая длительность разговоров днем | количественный |
| Total day calls | Общее количество звонков днем | количественный |
| Total day charge | Общая сумма оплаты за услуги днем | количественный |
| Total eve minutes | Общая длительность разговоров вечером | количественный |
| Total eve calls | Общее количество звонков вечером | количественный |
| Total eve charge | Общая сумма оплаты за услуги вечером | количественный |
| Total night minutes | Общая длительность разговоров ночью | количественный |
| Total night calls | Общее количество звонков ночью | количественный |
| Total night charge | Общая сумма оплаты за услуги ночью | количественный |
| Total intl minutes | Общая длительность международных разговоров | количественный |
| Total intl calls | Общее количество международных разговоров | количественный |
| Total intl charge | Общая сумма оплаты за международные разговоры | количественный |
| Customer service calls | Число обращений в сервисный центр | количественный |
Целевая переменная: Churn – Признак оттока, бинарный (1 – потеря клиента, то есть отток). Потом мы будем строить модели, прогнозирующие этот признак по остальным, поэтому мы и назвали его целевым.
Посмотрим на распределение целевого класса – оттока клиентов.
Выделим следующие группы признаков (среди всех кроме Churn ):
Посмотрим на корреляции количественных признаков. По раскрашенной матрице корреляций видно, что такие признаки как Total day charge считаются по проговоренным минутам (Total day minutes). То есть 4 признака можно выкинуть, они не несут полезной информации.
Теперь посмотрим на распределения всех интересующих нас количественных признаков. На бинарные/категориальные/порядковые признакие будем смотреть отдельно.
Видим, что большинство признаков распределены нормально. Исключения – число звонков в сервисный центр (Customer service calls) (тут больше подходит пуассоновское распределение) и число голосовых сообщений (Number vmail messages, пик в нуле, т.е. это те, у кого голосовая почта не подключена). Также смещено распределение числа международных звонков (Total intl calls).
Еще полезно строить вот такие картинки, где на главной диагонали рисуются распределения признаков, а вне главной диагонали – диаграммы рассеяния для пар признаков. Бывает, что это приводит к каким-то выводам, но в данном случае все примерно понятно, без сюрпризов.
Дальше посмотрим, как признаки связаны с целевым – с оттоком.
Построим boxplot-ы, описывающее статистики распределения количественных признаков в двух группах: среди лояльных и ушедших клиентов.
На глаз наибольшее отличие мы видим для признаков Total day minutes, Customer service calls и Number vmail messages. Впоследствии мы научимся определять важность признаков в задаче классификации с помощью случайного леса (или градиентного бустинга), и окажется, что первые два – действительно очень важные признаки для прогнозирования оттока.
Посмотрим отдельно на картинки с распределением кол-ва проговоренных днем минут среди лояльных/ушедших. Слева — знакомые нам боксплоты, справа – сглаженные гистограммы распределения числового признака в двух группах (скорее просто красивая картинка, все и так понятно по боксплоту).
Интересное наблюдение: в среднем ушедшие клиенты больше пользуются связью. Возможно, они недовольны тарифами, и одной из мер борьбы с оттоком будет понижение тарифных ставок (стоимости мобильной связи). Но это уже компании надо будет проводить дополнительный экономический анализ, действительно ли такие меры будут оправданы.
Теперь посмотрим на связь бинарных признаков International plan и Voice mail plan с оттоком. Наблюдение: когда роуминг подключен, доля оттока намного выше, т.е. наличие международного роуминга – сильный признак. Про голосовую почту такого нельзя сказать.
Наконец, посмотрим, как с оттоком связан категориальный признак State. С ним уже не так приятно работать, поскольку число уникальных штатов довольно велико – 51. Можно в начале построить сводную табличку или посчитать процент оттока для каждого штата. Но данных по каждом штату по отдельности маловато (ушедших клиентов всего от 3 до 17 в каждом штате), поэтому, возможно, признак State впоследствии не стоит добавлять в модели классификации из-за риска переобучения (но мы это будем проверять на кросс-валидации, stay tuned!).
Доли оттока для каждого штата:
Видно, что в Нью-Джерси и Калифорнии доля оттока выше 25%, а на Гавайях и в Аляске меньше 5%. Но эти выводы построены на слишком скромной статистике и возможно, это просто особенности имеющихся данных (тут можно и гипотезы попроверять про корреляции Мэтьюса и Крамера, но это уже за рамками данной статьи).
Подглядывание в n-мерное пространство с t-SNE
Построим t-SNE представление все тех же данных по оттоку. Название метода сложное – t-distributed Stohastic Neighbor Embedding, математика тоже крутая (и вникать в нее не будем, но для желающих – вот оригинальная статья Д. Хинтона и его аспиранта в JMLR), но основная идея проста, как дверь: найдем такое отображение из многомерного признакового пространства на плоскость (или в 3D, но почти всегда выбирают 2D), чтоб точки, которые были далеко друг от друга, на плоскости тоже оказались удаленными, а близкие точки – также отобразились на близкие. То есть neighbor embedding – это своего рода поиск нового представления данных, при котором сохраняется соседство.
Раскрасим полученное t-SNE представление данных по оттоку (синие – лояльные, оранжевые – ушедшие клиенты).
Видим, что ушедшие клиенты преимущественно «кучкуются» в некоторых областях признакового пространства.
Чтоб лучше понять картинку, можно также раскрасить ее по остальным бинарным признакам – по роумингу и голосовой почте. Синие участки соответствуют объектам, обладающим этим бинарным признаком.
Теперь понятно, что, например, много ушедших клиентов кучкуется в левом кластере людей с поключенным роумингом, но без голосовой почты.
Напоследок отметим минусы t-SNE (да, по нему тоже лучше писать отдельную статью):
И еще пара картинок. С помощью t-SNE можно действительно получить хорошее представление о данных (как в случае с рукописными цифрами, вот хорошая статья), а можно просто нарисовать елочную игрушку.
Домашнее задание № 2
Актуальные домашние задания объявляются во время очередной сессии курса, следить можно в группе ВК и в репозитории курса.
В качестве закрепления материала предлагаем выполнить это задание – провести визуальный анализ данных о публикациях на Хабрахабре. Проверить себя можно отправив ответы в веб-форме (там же найдете и решение).
Обзор полезных ресурсов
Статья написана в соавторстве с yorko (Юрием Кашницким).

