Часть вторая: моделирование данных

Ранее я провел EDA данных водяного насоса для DrivenData: Pump It Up Challenge. Теперь данные готовы к использованию в алгоритме машинного обучения для прогнозирования того, какие насосы могут работать, а какие нуждаются в ремонте.

Данные

Загрузка и подготовка данных

df = pd.read_csv(r'pump_model_data.csv')

print(df.columns)
Index(['funder', 'installer', 'scheme_management', 'extraction_type_group', 'quality_group', 'source_type', 'quantity', 'payment', 'basin', 'waterpoint_type', 'age_at_record', 'id', 'status_group', 'public_meeting', 'permit'], dtype='object')

Приведенный выше код загружается в файл .csv, созданный ранее, и перечисляет столбцы, присутствующие в фрейме данных. Многие алгоритмы машинного обучения не могут обрабатывать категориальные данные, поэтому данные должны быть преобразованы в числовые значения, чтобы модель могла понимать и извлекать полезную информацию.

Фиктивная кодировка

Функция .get_dummies() в Pandas используется для создания фиктивных переменных. Фиктивная переменная представляет собой числовое представление категориальных данных и может иметь значение либо 1, либо 0, где 1 представляет наличие категории, а 0 — отсутствие. Новый столбец создается для каждого уникального значения в кодируемом столбце, т. е. если столбец имеет 4 уникальных значения, будет создано 4 новых столбца.

Макеты были созданы для перечисленных выше столбцов и переупорядоченного фрейма данных для простоты использования в модели. Столбец status_group (указывающий, работает ли насос, не работает или работает, но нуждается в ремонте) затем преобразуется в числовые значения с помощью функции .cat.codes.

Поезд — Тест — Сплит

Обучение — тестирование — разделение — это процедура проверки, позволяющая оценить, насколько хорошо алгоритм машинного обучения будет работать при прогнозировании результатов новых данных. В этом случае обучающие данные будут разделены на 80% для обучения, 20% для проверки — проверка должна выполняться на данных, которые не использовались для обучения алгоритма, чтобы избежать смещения, отсюда и разделение. В следующем коде используется функция train_test_split() из библиотеки обработки данных scikit-learn.

Функция train_test_split создает переменные X_train, X_test, y_train и y_test. X_train и X_test содержат данные, из которых модель будет учиться (т. е. заранее подготовленную информацию о насосах), тогда как y_train и y_test являются метками для каждого насоса.

Случайный лесной классификатор

Алгоритмы контролируемого машинного обучения часто используются для классификации, то есть для прогнозирования того, к какому классу принадлежит наблюдение. Алгоритмы классификации включают Линейную регрессию, Машину опорных векторов, K-ближайших соседей, Наивный байесовский алгоритм, Деревья решений и Случайный лес. Эти алгоритмы используют различные методы классификации. Поскольку целью этого проекта является получение максимальной точности прогнозирования, я буду использовать классификатор случайного леса из-за его эффективности.

Деревья решений

Модели случайного леса строятся с использованием множества деревьев решений. Деревья решений состоят из узлов (листьев) и ветвей. Дерево начинается с набора данных в верхней части дерева. Затем этот набор данных разбивается на более мелкие подмножества в зависимости от особенностей данных. Затем процесс разделения повторяется для этих подмножеств, создавая дополнительный набор на основе другого признака данных. В каждом узле дерево наблюдает, какая функция позволяет разделить текущие наблюдения, чтобы сделать результирующие группы как можно более отличными друг от друга — и, наоборот, наблюдения в одной группе как можно более похожими.

В конце концов принимается решение прекратить разбивать набор данных на более мелкие группы. Эта конечная точка является листом дерева. Чтобы классифицировать точку, метки в листе подсчитываются. Если непомеченная точка достигает листа, она будет классифицирована как мажоритарная метка. Недостаток деревьев решений заключается в том, что они могут подгонять данные, когда модель слишком тесно подходит к обучающим данным, что делает ее неточной при прогнозировании результатов новых данных. Одним из способов борьбы с этим переоснащением является обрезка, подробное обсуждение которой можно найти в этой статье. Модели случайного леса могут избежать переобучения, поскольку они объединяют решения многих деревьев, поэтому любые переоснащенные деревья в лесу становятся менее эффективными — деревья защищают друг друга от своих индивидуальных ошибок.

Случайный лес

Случайный лес состоит из большого количества отдельных деревьев решений, работающих как ансамбль. Каждое отдельное дерево производит прогноз класса, и класс с наибольшим количеством объединенных голосов становится прогнозом модели.

Большое количество относительно некоррелированных моделей (деревьев), работающих как комитет, превзойдут любую из отдельных составляющих моделей.

Приведенная выше цитата из науки о данных (источник неизвестен) легко объясняет, почему метод случайного леса является хорошим выбором для задач классификации — они используют мудрость толпы!

Каждое дерево решений в случайном лесу создается с использованием другого подмножества точек данных из обучающих данных. Точки данных выбираются случайным образом с заменой — это означает, что точка данных может использоваться более чем в одном дереве решений и может быть выбрана более одного раза. Этот процесс называется упаковка. Для каждого дерева в случайном лесу исследуется случайно выбранное подмножество признаков, чтобы определить, какой из них претендует на лучший признак разделения. Как правило, если набор данных содержит n объектов, то для каждого дерева решений будет выбран квадратный корень из n объектов.

Модель

Чтобы использовать алгоритм Random Forest на наших данных, функция RandomForestClassifier() загружается из библиотеки Scikit-learn ансамбль.

from sklearn.ensemble import RandomForestClassifier

Начальный прогноз

В RandomForestClassifier() загружается n_estimators = 1000 со значением по умолчанию, равным 100. Параметр n_estimators — это количество деревьев, которые необходимо собрать перед выводом прогноза. Большее количество деревьев дает лучшую прогностическую эффективность, но модели требуется больше времени для обработки, при этом приращение времени увеличивается линейно с увеличением количества деревьев.

Модель подогнана к обучающим данным, а затем проверена с использованием тестовых данных из train_test_split.. Модель имела показатель успеха 0,7899 — она смогла правильно предсказать функциональное состояние помпы в 78,9% случаев.

Улучшение модели

В RandomForestClassifier() есть несколько гиперпараметров, которые можно настроить для повышения точности прогнозирования, включая количество деревьев, схему выборки и сложность каждого дерева. Я настроил несколько параметров, чтобы улучшить результат.

  • max_depth — это ограничивает максимальную глубину, до которой может достигать дерево. Ограничение глубины предотвращает переобучение — когда глубина дерева неограничена, оно может очень хорошо подходить для обучения, но не может достаточно хорошо обобщать, чтобы точно соответствовать тестовым данным. Я настроил свою модель на работу с диапазоном max_depth от 10 до 150. Наибольшая точность достигнута там, где max_depth = 21.

Хотя мы могли бы сами просмотреть все параметры, проверяя, какой из них дает наиболее точный результат, в sk-learn.model_selection есть функция, называемая GridSearchCV. GridSearchCV, которая по сути представляет собой модель перекрестной проверки, которая ищет наилучшие значения параметров из заданного набора.

  • Параметры n_estimators и max_depth обсуждались выше. n_estimators устанавливается ниже в GridSearchCV, чтобы сократить время расчета — оно может стать довольно большим.
  • max_features — это максимальное количество функций, которые модель может попробовать в отдельном дереве. Доступно несколько параметров, таких как sqrt или log, т. е. sqrt возьмет квадратный корень из общего количества доступных функций в наборе данных.
  • min_samples_split сообщает дереву решений в модели минимальное количество наблюдений, необходимых в любом заданном узле, чтобы разделить его.
  • min_samples_leaf указывает минимальное количество выборок, которые должны присутствовать в конечном узле после разделения узла.
  • Есть и другие гиперпараметры, которые можно настроить, но они здесь не обсуждаются.

Запуск GridSearchCV затратен с точки зрения времени и обработки, поэтому я запускал его поэтапно, изменяя каждый параметр отдельно. Были найдены следующие оптимальные параметры:

(n_estimators = 1000, max_features='sqrt', max_depth=20, min_samples_split=4,  min_samples_leaf=8)

Затем они были использованы в «лучшей» модели случайного леса. Однако при включении параметра min_samples_leaf успешность предсказания снижалась. Однако после удаления (установки значения по умолчанию) успех прогнозирования увеличился, что, вероятно, связано с тем, что модель переобучает данные обучения, когда min_samples_leaf выше.

best_forest_model = RandomForestClassifier(n_estimators = 1000, max_features='sqrt', max_depth=20, min_samples_split=4, random_state=12)#,  min_samples_leaf=8)
best_forest_model.fit(X_train, y_train)
print(best_forest_model.score(X_test, y_test))

Вышеуказанное дало оценку 0,7919.

Интерпретация результатов

Соревнование Pump It Up оценивает производительность модели, используя скорость классификации:

Приведенное выше вычисляет процент строк, в которых прогнозируемый класс ŷ в отправке соответствует фактическому классу y в тестовом наборе (где максимум равен единице, а минимум равен нулю). .

  • Матрица путаницы

Матрица путаницы используется для визуального выражения того, сколько предсказаний классификатор получил правильно или неправильно.

Строки матрицы путаницы представляют истинные метки, тогда как столбцы представляют предсказанные метки. Приведенная выше матрица нормализована, поэтому эти значения даны в процентах. По диагонали (сверху слева направо внизу) значения представляют собой процент случаев, когда прогнозируемая метка совпадает с истинной меткой. В других полях представлены случаи, когда классификатор неправильно пометил наблюдение — процент, в котором истинная функция была неправильно помечена как предсказанная функция.

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

  • Отчет о классификации
prediction=best_forest_model.predict(X_test)
print(classification_report(y_test, prediction))

Чтобы лучше понять производительность модели, мы можем использовать такие показатели, как точность, полнота и F1. Функция classification_report() из библиотеки scikit-learn умеет выводить эти метрики для заданной модели. Метрики учитывают истинные положительные результаты (tp), ложные положительные результаты (fp), истинные отрицательные результаты (tn) и ложные отрицательные результаты (fn).

  • Точность рассчитывается как tp / (tp + fp)и сообщает нам правильных положительных прогнозов по отношению к общему количеству положительных прогнозов.
  • Отзыв рассчитывается как tp / (tp + fn)и сообщает нам правильные положительные прогнозы по отношению к общему количеству фактических положительных результатов.
  • F1 – средневзвешенное гармоническое значение точности и полноты, рассчитанное по формуле 2 * (точность * полнота) / (точность + полнота). F1 используется в качестве быстрой метрики для оценки того, хорошо ли классификатор определяет, принадлежит ли точка данных к определенному классу, или он просто идентифицирует все как принадлежащее к доминирующему/крупному классу. Чем ближе к тому, что F1, тем выше производительность модели.
  • Точность рассчитывается по формуле (tp + tn) / (tp + tn + fp + fn) и является показателем, используемым DataDriven для оценки Успех модели классификации насосов.
                         precision    recall    f1-score   support
functional                  0.77       0.93      0.84       6457
functional needs repair     0.61       0.23      0.33       851
non-functional              0.86       0.71      0.78       4572
accuracy                                         0.79       11880
macro avg                   0.75      0.62       0.65       11880
weighted avg                0.79      0.79       0.78       11880

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

Водяные насосы, нуждающиеся в ремонте, классифицируются неправильно, что приводит к низкому показателю отзыва 0,23. Показатель точности, 0,61, выше, что означает, что относительно небольшое количество насосов неправильно помечено как нуждающиеся в ремонте.

Проблема с неправильной классификацией или идентификацией насосов, нуждающихся в ремонте, может быть связана с редкостью этого класса. По сравнению с исправными и неработающими насосами, в данных очень мало насосов, помеченных как требующие ремонта. Этот небольшой размер выборки усложняет модель идентификации таких насосов. Такие методы, как избыточная или недостаточная выборка, могут использоваться для улучшения успеха модели в определении редких классов, но это не будет обсуждаться здесь.

Важность функции

Модель может выводить, какие функции важны при прогнозировании состояния водяного насоса с использованием функции model.feature_importances_.

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

График показывает, что количество воды, доступной насосу (т. е. достаточное, недостаточное, сухое, сезонное), играет наиболее важную роль в прогнозах модели. Возраст насоса, тип водоразборной точки и метод извлечения также имеют большое значение. Собрания общественности играют относительно небольшую роль в классификации насосов, наряду с качеством воды и необходимостью разрешения. Хотя диаграмма важности функций представляет собой интересное резюме того, что модель узнала во время обучения, она не раскрывает, насколько хорошо эти важности сохраняются для новых данных.

Отправка результатов

DrivenData предоставил модели test_values_set.csv для прогнозирования состояния насосов. Затем эти прогнозы должны быть загружены в определенном формате, чтобы результаты могли быть оценены.

После отправки моих данных я получил оценку точности 0,7958, что дало мне рейтинг 3735 из 13921 участников.

Заканчивать

Вышеизложенное и моя предыдущая статья EDA описывают мою работу над вызовом DrivenData Pump It Up. Я очистил и исследовал данные и ввел их в модель классификатора случайного леса, результаты которой дали результат в пределах 27% лучших участников. Я очень доволен этим результатом, так как текущая победная заявка имеет показатель точности 0,8294.

Репозиторий GitHub для этой работы можно найти здесь.