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

«Вода — это жизнь, а чистая вода — здоровье». — Одри Хепберн

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

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

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

Оглавление

Рабочий процесс проекта

Постановка задачи

Как машинное обучение может помочь решить эту проблему?

О данных

Сопоставление проблемы реального мира с проблемой машинного обучения

Исследовательский анализ данных (EDA)

Обработка отсутствующих данных

Разработка функций

Выбор функции

Подготовка данных для обучения модели

Эксперименты с моделями машинного обучения

Развертывание лучшей модели в производстве

Будущая работа

Ссылки

Рабочий процесс проекта

Постановка задачи

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

Как машинное обучение может помочь решить эту проблему?

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

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

О данных

Источник данных: (drivendata.org)

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

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

Данные содержат 39 функций, которые предоставляют информацию о точке водоснабжения. Некоторые из этих функций —

  • amount_tsh - Общий статический напор (количество воды, доступное для точки водопоя)
  • date_recorded - Дата ввода строки
  • funder - Кто финансировал колодец
  • gps_height - Высота скважины
  • installer - Организация, установившая скважину
  • longitude - GPS-координата
  • latitude - GPS-координата
  • wpt_name - Название водоема, если он есть
  • num_private — (нет информации)
  • basin - Географический водоем
  • subvillage - Географическое положение
  • region - Географическое положение
  • water_quality - Качество воды

Ярлыки в этом наборе данных простые. Возможны три значения:

  • functional - водоразборный пункт исправен, ремонт не требуется
  • functional needs repair - водозабор работает, но требует ремонта
  • non functional - точка подачи воды не работает

Данные находятся в 3 файлах:

Мы можем создать единый кадр данных train_df, объединив train.csv и train_labels.csv.

Распределение меток классов в данных можно понять по следующей гистограмме, набор данных несбалансирован.

Отображение проблемы как проблемы машинного обучения

Прежде чем решать проблему машинного обучения, очень важно понять, что это за проблема машинного обучения. Инструменты и методы машинного обучения, необходимые для решения проблемы, могут меняться в зависимости от типа проблемы.

Тип проблемы машинного обучения

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

Показатели эффективности

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

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

Исследовательский анализ данных (EDA)

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

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

Давайте наденем наши детективные шапочки Шерлока, мы собираемся тщательно проверить данные.

Проверить повторяющиеся точки данных

Хорошие новости…! К счастью, у нас нет повторяющихся записей/строк в данных.

Отчет о качестве числовых данных

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

# Print Numerical Data quanlity
 numerical_dqr = get_numerical_dqr(numerical_cols, train_df)
 numerical_dqr

Наблюдения. Из приведенного выше отчета мы можем сделать следующие выводы:

  1. У суммы_tsh более 50% значений равны 0
  2. Минимальное значение gps_height равно -90, что невозможно, поскольку Танзания находится над уровнем моря и имеет положительные значения высоты.
  3. долгота 0 не существует в Танзании, неизвестные значения могут быть введены как 0
  4. num_private имеет множество значений, таких как 0, среднее значение около 0, а максимальное значение равно 1776, поэтому существует неопределенность в отношении этой функции.
  5. код_региона, код_района, год_строительства должны быть категориальными признаками.
  6. год постройки имеет 25% значений как 0, что невозможно, неизвестные значения могут быть введены как ноль

сумма_tsh

Он представляет собой общий статический напор, что означает высоту напорной трубы от дна водоотливного насоса. Изображение ниже может дать представление о amount_tsh.

Наблюдения:

  1. Около 70% записей имеют сумму_tsh = 0
  2. Значение 0 для статического напора не имеет смысла, поскольку нет необходимости использовать ручной насос для перекачивания воды при статическом напоре 0
  3. Можно сказать, пропущенные значения вводятся как 0
  4. Искусственное вменение 70% данных будет похоже на добавление систематической ошибки.
  5. Эта функция может быть удалена для обучения модели.

gps_height

gps_height представляет собой высоту точки воды над средним уровнем моря (MSL).

Поскольку у большинства записей gps_height = 0, проверим распределение записей с нулевым и ненулевым значением gps_height

Наблюдения:

  1. Около 37% точек имеют значение gps_height ‹= 0, что невозможно, поскольку Танзания расположена выше среднего уровня моря.
  2. Между распределением действительных и недействительных точек gps_height нет большой разницы.
  3. Неизвестные значения могли быть записаны как 0

широта и долгота

Нанесем на карту координаты долготы и широты. Мы можем использовать библиотеку folium для нанесения точек на карту.

Наблюдения:

  1. Некоторые точки расположены в южной части Атлантического океана, что невозможно. С этими координатами могли быть записаны неизвестные значения.
  2. Диапазон широт Танзании: от -11,8 до -2,00.
  3. Диапазон долготы Танзании: от 28 до 40,8.
  4. Точки вне этих диапазонов являются выбросами и могут быть удалены из данных.

num_private

К сожалению, информации о функции num_private от Taarifa не так много.

Наблюдение(я):

  1. Около 98,7% записей имеют значение num_private равное 0.
  2. Также нет информации об определении этой функции

Население

Он представляет собой количество людей, проживающих вблизи точки водоснабжения.

Наблюдения:

  1. Около 36% записей имеют нулевую популяцию. Наличие такого количества точек водоснабжения с нулевым населением кажется нелогичным. Неизвестные значения могут быть записаны как 0.
  2. Существует большая дисперсия в распределении населения от 0 до 30500.

Корреляция между числовыми признаками

Наблюдения:

  1. Нет очень сильно коррелированных признаков.
  2. District_code и region_code кажутся положительно коррелированными, так как они похожи, поэтому рассматривать их как категориальные было бы идеально.
  3. широта и долгота имеют отрицательную корреляцию, и обе они важны, поскольку вместе определяют местоположение точки водоема.

Категориальный отчет о качестве данных

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

Давайте проверим категориальные функции один за другим.

бассейн

Представляет собой географический водный бассейн водоема. Бассейны - это участки земли, которые стекают в определенный водоем, такой как озеро, ручей, река или устье.

область

Представляет названия различных регионов Танзании. Мы можем нанести на карту региональное распределение статусов водоемов.

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

код_региона и код_района

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

Функции region, region_code и District_code собирают аналогичную информацию о географических регионах Танзании, но с разной степенью детализации. Любая из них может быть полезна в задаче прогнозирования, поскольку они похожи.

лга

Снова географический объект, представляющий регионы Танзании, закодированные числовыми значениями.

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

public_meeting

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

В 91% записей были публичные встречи, а у насосов без собраний сравнительно высокий процент неработающих насосов. Отсутствует около 5% значения, которое можно разумно рассчитать, используя приведенное выше наблюдение.

схема_управление

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

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

тип_извлечения и класс_типа_извлечения

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

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

платеж и тип_платежа

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

Наблюдения:

  1. payment и payment_type собирают одну и ту же информацию с разными именами для категорий
  2. оба показывают одинаковое распределение по меткам классов
  3. Любой из них может быть использован для обучения модели.

вода_качество и группа качества

Они представляют качество воды в виде различных категорий. например. молочный, мягкий, соленый и т.д.

Наблюдения:

  1. water_quality и quality_group собирают одну и ту же информацию на разных уровнях детализации.
  2. water_quality показывает лучшее распределение по меткам классов
  3. Любой из них, предпочтительно water_quality, можно использовать для обучения модели.

количество и количество_групп

Эти характеристики представляют собой количество воды, доступной в точке водоснабжения, в виде различных категорий, таких как сухая, сезонная, недостаточная и т. д.

Наблюдения:

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

источник и источник_тип

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

Наблюдения:

  1. source и source_type собирают одну и ту же информацию на разных уровнях детализации.
  2. source_type показывает лучшее распределение по меткам классов
  3. насосы с плотиной, скважиной, неглубокой скважиной как source_type, как правило, более нефункциональны, это может быть интересное понимание
  4. Любой из них, предпочтительно функция source_type, может использоваться для обучения модели.

исходный_класс

Это еще одна функция, основанная на источнике. Это более высокоуровневая версия source_type только с 3 категориями: грунтовые воды, поверхностные и неизвестные.

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

waterpoint_type и waterpoint_type_group

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

Наблюдения:

  1. waterpoint_type и waterpoint_type_group собирают одну и ту же информацию на разных уровнях детализации.
  2. waterpoint_type показывает лучшее распределение по меткам классов
  3. Любая функция, предпочтительно тип waterpoint_type, может использоваться для обучения модели.

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

строительство_год

Он представляет собой год, в котором был построен пункт водоснабжения. Он имеет 55 уникальных значений в диапазоне от 0 до 2013.

Наблюдения:

  1. Водозаборы, построенные ранее, имеют более низкий процент работающих насосов, что является полезным советом.
  2. 35% записей имеют значение года постройки равное 0, что может быть введено как отсутствующее значение.
  3. 27% водозаборов были построены в период с 2000 по 2012 год, что может быть полезно для условного исчисления данных.
  4. Используя функции record_date и building_year, можно создать новую полезную функцию.

date_recorded можно проанализировать, разделив его на две новые функции с именами record_year и record_month.

# Converting date_recorded dtype to datetime format
train_df_date = train_df.copy()
train_df_date['date_recorded'] = pd.to_datetime(train_df['date_recorded'])

# Creating separate year and month columns
train_df_date['record_year'] = train_df_date['date_recorded'].dt.year
train_df_date['record_month'] = train_df_date['date_recorded'].dt.month

Record_year

record_year представляет год, в котором была сделана запись в наборе данных для этой конкретной точки водоема.

Наблюдения:

  1. 2002 год имеет только 1 запись
  2. 2011, 2012, 2013 год имеют около 99% записей
  3. Функции record_year и Construction_year можно использовать для создания новой функции, связанной с возрастом водоема, которая может оказаться полезной.

Record_month

record_month представляет месяц, в котором была сделана запись в наборе данных для этой конкретной точки водоема.

Наблюдения:

  1. Дожди в Танзании:
heaviest rains (called masika) -> mid-March to May
shorter period of rain -> November to mid-January

2. Сезоны в Танзании:

dry season -> May to October
summer -> November to March

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

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

Многомерный анализ

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

Парные участки

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

Наблюдения:

Для различных комбинаций числовых признаков

  1. Точки всех меток класса показывают большое количество перекрытий
  2. PDF всех переменных также сильно перекрываются
  3. Парный график широты и долготы показывает очень плотно упакованную карту рассеяния.
  4. Точки не легко отделимы

Параллельные графики координат для amount_tsh, gps_height, долготы, широты, num_private, населения

Наблюдения:

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

Совместный график между широтой и долготой

Наблюдения:

  1. Некоторые записи имеют отдаленные значения долготы и широты.
  2. Pdf-файлы широты и долготы показывают перекрытие, поскольку точки водопоя со всеми 3 метками классов присутствуют в местах по всей Танзании.

Совместный график между gps_height и населением

Наблюдения:

  1. Большинство точек имеют значения, близкие к нулю.
  2. точки показывают перекрытие, поэтому их нелегко разделить

gps_height против количества против меток класса

Наблюдения:

  1. По всем трем меткам классов во многих точках значение gps_height близко к 0.
  2. Точки с сухим и неизвестным количеством воды показали различное распределение gps_height по меткам классов. Другие категории показали почти одинаковое распределение по всем трем меткам классов.

количество по сравнению с исходным_классом по сравнению со статусной_группой

Наблюдения:

  1. Будь то любой источник, сухие источники воды, как правило, не работают.
  2. Водоемов с подземными водами больше, чем с поверхностными.
  3. Водозаборы с сезонным количеством воды более функциональны для источника подземных вод, чем для источника поверхностных вод.

TSNE Визуализация числовых данных

TSNE — это инструмент визуализации, который используется для визуализации многомерных данных, которые отображают точки в многомерном пространстве в более низкие измерения, 2D или 3D. TSNE пытается приблизить похожие точки друг к другу. Недоумение и взаимодействия — это два параметра TSNE, которые можно настроить для получения наилучшей визуализации в более низких измерениях.

tsne = TSNE(perplexity=p, n_iter=500).fit_transform(temp)

#plot 2d tsne embedding
temp_dict = {'0': tsne[:, 0], '1': tsne[:, 1], "status_group":train_df["status_group"].values}
temp_df = pd.DataFrame(temp_dict)
plt.figure(figsize=(10,10))
sns.scatterplot(x="0", y="1", hue="status_group", data=temp_df)
plt.title(f"perplexity={p}")

Мы можем попробовать визуализировать данные для различных значений недоумения и итерации. Попробовав разные параметры, мы получили визуализацию выше с недоумением = 120 и итерацией = 500.

Наблюдения:

  1. При более высоком недоумении встречаются кластерные образования.
  2. Кластеры не представляют какую-то одну конкретную метку класса, в кластерах существует огромное перекрытие меток классов.

Резюме ЭДА

  1. У суммы_tsh и num_private для большинства записей значения равны 0.
  2. некоторые функции, такие как Funder, Installer, wpt_name, subvillage, ward, Scheme_name, имеют очень большое количество элементов.
  3. Некоторые пары функций, например платеж, payent_type и т. д., собирают одну и ту же информацию на разных уровнях детализации.
  4. Некоторые новые функции могут быть разработаны с использованием функций, основанных на дате.
  5. Некоторые функции, например. gps_height и долгота имеют недопустимые значения, которые могут быть заменены или отброшены.
  6. Некоторые признаки имеют пропущенные значения, которые могут быть импутированы.
  7. Некоторые функции показывают хорошее распределение по меткам классов, что может быть очень полезно в задаче прогнозирования.

Обработка отсутствующих данных

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

В свойствах public_meeting, Scheme_management, Permit и Construction_year отсутствуют значения. Все они являются категориальными функциями, отсутствующие значения могут быть импутированы с использованием значения режима функции, как показано в приведенном ниже примере для схемы_управления.

# Strategy -> mode value imputation
SCHEME_MANAGEMENT_MODE = str(train_df['scheme_management'].mode())
imputed_df['scheme_management'] = imputed_df['scheme_management'].fillna(SCHEME_MANAGEMENT_MODE)

Construction_year имеет отсутствующие значения, но это числовая функция, которую мы можем использовать для понимания EDA, чтобы сделать некоторые умные расчеты. Из EDA мы узнали, что около 27% водозаборов были построены в период с 2000 по 2012 год, мы можем вычислить значения nan путем случайного выбора значения между 2000 и 2012 годами.

# Strategy -> 27% waterpoints were constructed between 2000 to 2012, we can
#             randomly impute values between 2000 to 2012

def year_imputer(x):
    """
    Function to impute construction_year in data
    """
    if x == 0:
        year_range = list(range(2000, 2013))
        impute_year = random.choice(year_range)
        return impute_year

imputed_df['construction_year'] = np.where(imputed_df['construction_year'] == 0,
                                           imputed_df['construction_year'].apply(year_imputer),
                                           imputed_df['construction_year'])

Вменение недопустимых значений

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

gps_height

Некоторые значения записаны как 0, что невозможно, поскольку Танзания расположена выше среднего уровня моря. Мы должны условно вычислить эти значения, так как это числовая характеристика, мы можем использовать медианное значение для условного исчисления.

# Strategy -> median value imputation
GPS_HEIGHT_MEDIAN = train_df['gps_height'].median()
imputed_df['gps_height'] = np.where(imputed_df['gps_height'] <= 0, GPS_HEIGHT_MEDIAN, imputed_df['gps_height'])

широта

Диапазон широт Танзании составляет от -11,8 до -1,00. Любые значения за пределами этого диапазона могут быть рассчитаны по среднему значению широты.

# Strategy -> median value imputation
LATITUDE_MEDIAN = train_df['latitude'].median()
imputed_df['latitude'] = np.where(imputed_df['latitude'] < -11.8, LATITUDE_MEDIAN, imputed_df['latitude'])
imputed_df['latitude'] = np.where(imputed_df['latitude'] > -1, LATITUDE_MEDIAN, imputed_df['latitude'])

долгота

Диапазон долготы Танзании составляет от 29,00 до 40,80. Любые значения за пределами этого диапазона могут быть рассчитаны по среднему значению долготы.

# Strategy -> median value imputation
LONGITUDE_MEDIAN = train_df['longitude'].median()
imputed_df['longitude'] = np.where(imputed_df['longitude'] < 29, LONGITUDE_MEDIAN, imputed_df['longitude'])
imputed_df['longitude'] = np.where(imputed_df['longitude'] > 40.8, LONGITUDE_MEDIAN, imputed_df['longitude'])

Разработка функций

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

Здесь новые функции с именами «record_month» и «record_year» могут быть получены из функции date_recorded. Новая функция под названием «waterpoint_age» может быть спроектирована с использованием функций building_year и date_recorded.

# Creating separate year and month columns
imputed_df['date_recorded'] = pd.to_datetime(imputed_df['date_recorded'])
imputed_df['record_year'] = imputed_df['date_recorded'].dt.year
imputed_df['record_month'] = imputed_df['date_recorded'].dt.month
imputed_df['waterpoint_age'] = imputed_df['record_year'] - imputed_df['construction_year']

# treating record_year and record_month as categorical features
imputed_dropped_df[['record_year', 'record_month']] = imputed_dropped_df[['record_year', 'record_month']].astype('object')

Выбор функции

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

#Drop less useful features
dropped_features = ['id', 'amount_tsh', 'funder', 'installer', 'wpt_name',
                    'num_private', 'subvillage', 'region_code', 'district_code',
                    'ward', 'recorded_by', 'scheme_name', 'construction_year', 
                    'extraction_type', 'extraction_type_group', 'management_group', 
                    'payment', 'quality_group', 'quantity_group', 'source', 
                    'waterpoint_type_group', 'date_recorded']
imputed_dropped_df = imputed_df.drop(dropped_features, axis=1)

Тренировочный тестовый сплит

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

# Separate features and label
y = imputed_dropped_df['status_group']
X = imputed_dropped_df.drop(['status_group'], axis=1)

# Make train test split
test_size = 0.2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42, stratify=y)
print(f"X_train shape: {X_train.shape}")
print(f"X_test shape: {X_test.shape}")
print(f"y_train shape: {y_train.shape}")
print(f"y_test shape: {y_test.shape}")

Подготовка данных для обучения модели

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

Здесь данные имеют числовые и категориальные характеристики. Числовые признаки должны быть нормализованы, а категориальные признаки должны быть правильно закодированы перед обучением модели.

нормализация числовых данных

Числовые признаки можно нормализовать с помощью мин-макс масштабатора от Scikit Learn. Пример нормализации показан ниже, аналогичным образом можно нормализовать все числовые признаки.

#Normalizing gps_height
gps_height_normalizer = MinMaxScaler()
gps_height_normalizer.fit(X_train['gps_height'].values.reshape(-1, 1))
train_gps_height_normalized = gps_height_normalizer.transform(X_train['gps_height'].values.reshape(-1, 1))
test_gps_height_normalized = gps_height_normalizer.transform(X_test['gps_height'].values.reshape(-1, 1))
joblib.dump(gps_height_normalizer, "gps_height_normalizer")

Мы сохраняем нормализатор gps_height, потому что позже мы собираемся выполнить тот же процесс с тестовыми данными, прежде чем получать прогнозы.

кодирование категориальных данных

Категориальные функции можно кодировать с помощью One Hot Encoder от Scikit Learn. Пример One Hot Encoding показан ниже, аналогичным образом могут быть закодированы все категориальные признаки.

#Encoding record_month
record_month_encoder = OneHotEncoder(handle_unknown = 'ignore')
record_month_encoder.fit(X_train['record_month'].values.reshape(-1, 1))
train_record_month_encoded = record_month_encoder.transform(X_train['record_month'].values.reshape(-1, 1))
test_record_month_encoded = record_month_encoder.transform(X_test['record_month'].values.reshape(-1, 1))
joblib.dump(record_month_encoder, "record_month_encoder")

Мы сохраняем record_month_encoder, потому что позже мы собираемся выполнить тот же процесс с тестовыми данными, прежде чем получать прогнозы.

После обработки данных мы можем сложить данные горизонтально, чтобы создать разреженную матрицу.

X_train_encoded = hstack([train_gps_height_normalized, train_longitude_normalized, train_latitude_normalized,
                        train_basin_encoded, train_region_encoded, train_lga_encoded, train_population_normalized,
                        train_public_meeting_encoded, train_scheme_management_encoded, train_permit_encoded,
                        train_extraction_type_class_encoded, train_management_encoded, train_payment_type_encoded,
                        train_water_quality_encoded, train_quantity_encoded, train_source_type_encoded,
                        train_source_class_encoded, train_waterpoint_type_encoded, train_record_year_encoded,
                        train_record_month_encoded, train_waterpoint_age_normalized]).tocsr()

X_test_encoded = hstack([test_gps_height_normalized, test_longitude_normalized, test_latitude_normalized,
                        test_basin_encoded, test_region_encoded, test_lga_encoded, test_population_normalized,
                        test_public_meeting_encoded, test_scheme_management_encoded, test_permit_encoded,
                        test_extraction_type_class_encoded, test_management_encoded, test_payment_type_encoded,
                        test_water_quality_encoded, test_quantity_encoded, test_source_type_encoded,
                        test_source_class_encoded, test_waterpoint_type_encoded, test_record_year_encoded,
                        test_record_month_encoded, test_waterpoint_age_normalized]).tocsr()

метки классов кодирования

Метка класса имеет несколько значений, которые можно закодировать как отдельные целочисленные значения. Кодирование меток классов с помощью scikit Learn Label Encoder.

#Encode class labels
label_encoder = LabelEncoder()
label_encoder.fit(y_train.values.reshape(-1,1))
y_train_encoded = label_encoder.transform(y_train.values.reshape(-1,1))
y_test_encoded = label_encoder.transform(y_test.values.reshape(-1,1))
joblib.dump(label_encoder, "label_encoder")

Эксперименты с моделью машинного обучения

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

Простая базовая модель

Давайте рассмотрим тупую модель, которая предсказывает только одну метку класса, т.е. «функциональный», который является меткой большинства классов в наших данных. С этой моделью мы получаем micro_f1_score равным 0,5431. Наша модель машинного обучения должна работать лучше, чем эта глупая модель.

Базовая модель машинного обучения

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

Различные модели

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

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

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

Классы Scikit Learn GridSearchCV или RandomSearchCV можно использовать для проверки производительности модели при различных настройках гиперпараметров. Модель с лучшими характеристиками может быть выбрана как лучшая модель.

Настройка классификатора случайного леса

# Hyper Parameter Tuning for Random Forest Classifier

# Estimator
rfc = RandomForestClassifier(random_state=42, warm_start=True)

# params
params = {"n_estimators": [100, 150, 200, 250],
          "max_depth": [6, 10, 20, 24, 40, 60],
          "min_samples_split": [4, 6, 8, 10],
          "max_features": ['sqrt', 'log2']}

grid_cv = GridSearchCV(rfc, params, scoring=["f1_micro", "roc_auc_ovr"],
                      n_jobs=-1, cv=6, refit="f1_micro")

grid_cv.fit(X_train_encoded, y_train_encoded)

Мы все еще можем поэкспериментировать с параметром n_estimators и проверить, улучшится ли производительность.

# best n_estimators
estimators = [200, 250, 280, 300, 320, 350, 380]
train_f1 = []
test_f1 = []

for i in estimators:
    rf = RandomForestClassifier(max_depth=40,
                                max_features='sqrt',
                                min_samples_split=8,
                                n_estimators=i,
                                random_state=42,
                                warm_start=False)

    # Training
    rf.fit(X_train_encoded, y_train_encoded)

Наблюдения:

Мы получили лучшие гиперпараметры для классификатора случайного леса как

  1. максимальная_глубина=40,
  2. max_features=’sqrt’
  3. min_samples_split=8,
  4. n_оценщиков = 280

Лучшая модель классификатора случайного леса

# Best Random Forest Model
best_rf = RandomForestClassifier(max_depth=40,
                                 max_features='sqrt',
                                 min_samples_split=8,
                                 n_estimators=280,
                                 random_state=42,
                                 warm_start=False)

# Training
best_rf.fit(X_train_encoded, y_train_encoded)

Настройка классификатора CatBoost

# Hyper Parameter Tuning for Random Forest Classifier

# Estimator
cbc = CatBoostClassifier(random_seed=42)

# params
params = {"iterations": [100, 150, 200, 250],
          "depth": [4, 6, 10, 12, 14, 16],
          "learning_rate": [0.1, 0.2, 0.3, 0.5]}

rand_cv = RandomizedSearchCV(cbc, params, scoring="f1_micro", n_iter=35,
                      n_jobs=-1, cv=6, refit=True)

rand_cv.fit(X_train_encoded, y_train_encoded)
# best iterations
estimators = [200, 250, 280, 300, 320, 350, 380, 400, 420, 450]
train_f1 = []
test_f1 = []

for i in estimators:
    cbc = CatBoostClassifier(learning_rate=0.2, 
                            iterations = i,
                            depth=10, 
                            random_seed=42,
                            verbose=0)


    # Training
    cbc.fit(X_train_encoded, y_train_encoded)

Наблюдения:

Мы получили лучшие гиперпараметры для классификатора случайного леса как

  1. скорость_обучения = 0,2
  2. итераций = 380
  3. глубина=10

Лучшая модель классификатора CatBoost

# best cat boost model
best_cbc = CatBoostClassifier(learning_rate=0.2, 
                         iterations = 380,
                         depth=10, 
                         random_seed=42,
                         verbose=0)

# Training
best_cbc.fit(X_train_encoded, y_train_encoded)

Тюнинговый классификатор бэггинга

# Hyper Parameter Tuning for Bagging Classifier

# Estimator
bgc = BaggingClassifier(random_state=42)

# params
params = {'n_estimators': [100, 200, 300, 400, 500, 600],
          'max_features': [0.2, 0.25, 0.3, 0.35, 0.4]}

bagging_cv = GridSearchCV(bgc, params, scoring=["f1_micro", "roc_auc_ovr"], cv=5,
                       refit="f1_micro")

bagging_cv.fit(X_train_encoded, y_train_encoded)

Наблюдения:

Лучшие параметры модели классификатора упаковки

  1. «max_features»: 0,4
  2. «n_оценщиков»: 400

Лучшая модель классификатора пакетов

# Best bagging classifier model
best_bgc = BaggingClassifier(n_estimators = 400,
                             max_features = 0.4)

# training
best_bgc.fit(X_train_encoded, y_train_encoded)

Классификаторы голосования

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

# Estimators
best_rf = RandomForestClassifier(max_depth=40,
                                 max_features='sqrt',
                                 min_samples_split=8,
                                 n_estimators=280,
                                 random_state=42,
                                 warm_start=False)

best_xgbc = XGBClassifier(colsample_bytree =0.3,
                          eta=0.2,
                          max_depth=12,
                          n_estimators=280,
                          objective='multi:softprob',
                          random_state=42)

best_cbc = CatBoostClassifier(learning_rate=0.2, 
                         iterations = 350,
                         depth=10, 
                         random_seed=42,
                         verbose=0)

best_bgc = BaggingClassifier(n_estimators = 400,
                             max_features = 0.4)

estimators = [("rf", best_rf), ("xgbc", best_xgbc), ("cbc", best_cbc),
              ("bgc", best_bgc)]

# training
mvc = VotingClassifier(estimators=estimators, voting='soft')
mvc.fit(X_train_encoded, y_train_encoded)

При создании проекта я поэкспериментировал еще с некоторыми моделями. Подробная информация об экспериментах с моделью ноутбука Jupyter доступна на GitHub.

Сводка модельных экспериментов

Подытожим характеристики всех моделей.

Лучшая модель:

Классификатор случайного леса

Параметры:

  1. максимальная_глубина=40,
  2. max_features=’sqrt’
  3. min_samples_split=8,
  4. n_оценщиков = 280

Показатели эффективности:

  1. train_micro_f1_score = 0,9104
  2. test_micro_f1_score = 0,8132

Развертывание лучшей модели в производстве

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

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

Здесь мы развернем нашу систему прогнозирования как отдельное веб-приложение, работающее в облаке. Веб-фреймворк для приложения можно создать с помощью библиотеки Python Streamlit. Разработанный веб-фреймворк можно развернуть в облачном сервисе Streamlt.

Ссылка на приложение: https://theingale-pump-status-prediction-app-lov2d4.streamlit.app/

Ссылка на GitHub: https://github.com/theingale/Pump-status-prediction

Будущая работа

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

Ссылки

  1. https://www.drivendata.org/competitions/7/pump-it-up-data-mining-the-water-table/page/23/
  2. waterdamagedefense.com
  3. Многомерный анализ
  4. https://www.latlong.net/
  5. https://cs229.stanford.edu/proj2018/report/106.pdf
  6. https://www.nathab.com/know-before-you-go/african-safaris/east-africa/weather-climate/tanzania/

Спасибо, что читаете блог…! С любыми отзывами и предложениями вы можете связаться со мной в Linkedin по адресу https://www.linkedin.com/in/saket-ingale/.