Все коды аккуратно задокументированы на https://github.com/seho0808/rf_hospital_medium

Вы можете свободно копировать и использовать любой мой код из этой статьи.

I. О чем эта статья?

Представьте, что вы пытаетесь выбрать ближайшую к вам больницу. Вы видите три больницы на картах Google, но только одна из них имеет рейтинги. Что бы вы сделали?

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

Из этой статьи вы узнаете:

  1. Как использовать и настраивать случайный лес с библиотекой Scikit-Learn.
  2. Элементарный пример извлечения признаков и очистки данных для больших наборов данных. Мы объединим множество файлов, содержащих объясняющие переменные (X), с одинаковыми идентификаторами больниц.
  3. Пример использования машинного обучения в сфере здравоохранения.

Если вы хотите №1, я рекомендую прочитать разделы II и IV.

Если вы хотите №2, я рекомендую прочитать разделы II и III.

II. Набор данных

Все наборы данных взяты с https://data.medicare.gov/data/hospital-compare. Список используемых файлов:

Hvbp_tpstps07_2017.csv
Hvbp_hcahpstps07_2017.csv
Timely and Effective Care — Hospital.csv
Medicare Hospital Spending per Patient — Hospital.csv
Outpatient Imaging Efficiency — Hospital.csv
Payment and Value of Care — Hospital.csv
Complications and Deaths — Hospital.csv
Ambulatory Surgical Measures-Facility.csv
Readmission Reduction.csv
Healthcare Associated Infections — Hospital.csv

Мы собираемся попытаться предсказать две меры баллов. Это столбцы total_performance_score и overall_hospital_performance_rating. Эти две меры взяты из Hvbp_tpstps07_2017.csv и Hvbp_hcahpstps07_2017.csv соответственно. Мы собираемся использовать все другие наборы данных в качестве переменных-предикторов, которые используются для прогнозирования переменных Y, которыми в данном случае являются total_performance_score и overall_hospital_performance_rating .

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

Итак, как мы собираемся предсказать tps? Давайте взглянем на набор данных Hvbp_tpstps07_2017.csv .

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

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

Здесь важно отметить, что для каждого набора данных существуют различные меры оценки. Возьмем, к примеру, набор данных «Своевременная и эффективная помощь». На рисунках ниже и выше показаны одни и те же строки, но разные столбцы. Вы можете видеть сверху несколько строк с одинаковыми идентификаторами больниц. Здесь несколько строк, потому что существует множество показателей для оценки своевременной и эффективной помощи в больнице. Посмотрите на рисунок ниже. Столбец measure_id сообщает нам, к какой метрике оценки относится столбец score. Например, здесь идентификатор больницы 10001 содержит 255 для показателя ED1 и 84 для показателя ED2.

Фактически, для этого набора данных, похоже, существует 21 уникальный показатель для оценки своевременной и эффективной помощи в больнице. Если бы было десять больниц, было бы 210 рядов. Мы попытаемся объединить все эти разные меры из разных наборов данных в Разделе III. Затем мы обучим нашу случайную модель леса с этим объединенным набором данных, чтобы предсказать наши Ys. Позже мы получим 101 столбец в объединенном наборе данных, и все они относятся к различным показателям для оценки больниц.

III. Извлечение функций / очистка данных

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

Начнем с извлечения признаков из m Timely and Effective Care — Hospital.csv.. Этот файл содержит показатели своевременного и эффективного лечения для каждой больницы с 21 различными показателями.

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

Из подзаголовков мы видим, что третий подзаголовок (график в (1,3) в матрице подзаголовков) показывает прямую линию. На что это указывает? Прямая линия означает, что все значения x постоянны. Фактически все значения равны нулю. Следовательно, мы удалим этот бесполезный столбец из нашего набора данных. Мы также исключили другие меры в следующих местах: (2,1), (2,3), (3,5), (5,1). Подграфик записывается слева направо и сверху вниз, так что они равны 6, 8, 15, 21 соответственно в порядке измерения. Мы говорим, что эти меры бесполезны, потому что большинство точек находятся в нуле. Они дают гораздо меньше информации и почти не помогают нашей модели. Эти меры в дальнейшем почти не уменьшили бы наши остатки от нашей модели. После удаления бесполезных столбцов у нас теперь есть только полезные столбцы в объединенном наборе данных full_XY, как показано ниже.

Теперь перейдем к новому набору данных. Давайте теперь посмотрим на набор данных Medicare Hospital Spending per Patient — Hospital.csv.

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

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

Единственным исключением является набор данных Ambulatory Surgical Measures-Facility.csv. Мы должны пренебречь всем набором данных, поскольку его идентификаторы и названия больниц плохо совпадают с другими наборами данных.

На последнем этапе мы выводим очищенный набор данных в виде нового файла .csv.

IV. Подгонка и настройка модели случайного леса

Подгонка модели случайного леса

Коды для построения модели находятся в отдельном файле записной книжки Jupyter на GitHub. Я настоятельно рекомендую вам сначала взглянуть на исходный файл на GitHub. Мы подгоняем модель случайного леса с уже настроенными параметрами. Подробнее о настройке я расскажу позже в этом разделе.

Мы собираемся определить нашу функцию для одного прогона случайного леса. Строка 4 и 5 делит все независимые переменные на X и столбец, который мы пытаемся предсказать, как y. Мы используем функцию train_test_split из библиотеки Scikit-Learn. Это одна строка кода в строке 9. Мы устанавливаем test_set=0.05, что означает, что 95% данных будут данными обучения, а 5% - данными тестирования. Каждый запуск будет формировать разные подмножества обучающих и тестовых данных, поскольку мы случайным образом устанавливаем random_state. Однако вы можете отключить random_state, установив дополнительный параметр shuffle=false, который полностью игнорирует все, что вы добавляете в параметр random_state. Сохранение определенного числа random_state (т.е. random_state = 5) позволит вам случайным образом перемешивать данные, но сохраняя порядок при каждом запуске.

В строках с 11 по 15 мы строим модель и прогнозируем наш тестовый набор .max_depth parameter ограничивает глубину всех деревьев решений. Параметр n_estimator устанавливает, из скольких деревьев решений мы хотим выполнить пакетирование. Здесь я остановлюсь на настройке параметров. Если вы не знакомы с основами Random Forest, https://towardsdatascience.com/understanding-random-forest-58381e0602d2 - отличный источник для понимания основ.

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

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

В этом примере мы не будем использовать перекрестную проверку в k-кратном размере, но мы будем использовать самозагрузку. Их результаты почти схожи, но с той лишь разницей, что перекрестная проверка исчерпывает набор данных (использует каждую отдельную точку данных в наборе данных хотя бы один раз в качестве обучающих данных и хотя бы один раз в качестве тестовых данных). Мы уже установили разбиение тест / поезд как случайное из кода функции случайного леса, поэтому мы должны запускать функцию несколько раз для начальной загрузки. Если вы хотите выполнить k-кратную перекрестную проверку или другие вещи, вам нужно будет изменить разделенную часть (строки 7 ~ 9 в функции случайного леса). Затем мы сохраняем все показатели из нашей модели.

Мы распечатываем среднее значение по всем бутстрапированным моделям и смотрим результат. Если вы не знакомы с приведенными выше показателями, я предлагаю их поискать. Самый простой и удобный для интерпретации - это показатель MAE (средняя средняя ошибка), который показывает, что каждый раз, когда мы прогнозируем, возникает ошибка около ± 6,712.

Если мы проведем такой же анализ для оценки HCAHPS вместо оценки tps, мы получим этот результат. Этот показатель HCAHPS - это тот, который я упомянул в Разделе I, второй показатель рейтинга, который мы собирались предсказать. Распределение оценок сильно отличается от tps. У нас гораздо меньшая дисперсия в распределении.

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

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

Ограничение максимальной глубины - это самая важная вещь, которую я извлек из использования Random Forest в пяти больших проектах. Это называется обрезкой. Случайный лес очень подвержен переобучению, поскольку в худшем случае ветви могут разделиться на конечный узел (также известный как листовые узлы), который полностью переоборудован на одно значение. Параметры Scikit-Learn по умолчанию максимально предотвращают попытки пользователя переобучить модель. Однако параметр max_depth по умолчанию установлен на максимальное значение. Вы должны обрезать свое дерево, чтобы оно лучше подходило. Отсутствие обрезки дерева приведет к лучшему соответствию вашим обучающим данным. Однако для тестовых данных ваши показатели точности пострадают.

Итак, как обрезать дерево? Это просто. Сравним max_depth=8 и max_depth=40. Для max_depth=8 мы получаем около 6,7 для MAE для прогнозирования tps. Однако max_depth=40 дает около 6,8. В нашем примере это немного лучший результат.

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

А как насчет других параметров? Я говорю, что вторым по важности параметром является n_estimator , который представляет собой количество деревьев решений, по которым нужно выполнить пакетирование. Для больших наборов данных вы можете увеличить количество оценщика. Его легко настроить, так как он никогда не станет хуже для большего количества оценщиков. Вам просто нужно выбрать наименьшее значение, где точность перестает повышаться. Я видел, как много людей путали с max_features. Этот параметр в основном предназначен для скорости вычислений. Я рекомендую оставить его как вариант по умолчанию, если ваш набор данных не слишком велик, а для запуска модели требуется вечность.

V. Заключение / соображения

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

  1. Наша модель ориентирована только на больницы, в которых есть меры TPS.
  2. Одной из сильных сторон нашей модели является то, что мы сделали все значения NA равными 0, а случайный лес разветвлял их как недоступные. Это может привести к странной интерпретации нескольких пограничных точек между 0 и фактическими значениями. Однако мы все равно получаем гораздо лучший результат, чем исключение всех строк NA.
  3. Мы можем проанализировать выбросы. (Какие больницы было трудно предсказать?)
  4. Мы можем использовать важность функции. (Я сделаю это в Разделе VI в качестве дополнительного раздела.)

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

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

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

Мы видим, что несколько баллов очень важны для прогнозирования общей оценки производительности. Мы получаем почти такую ​​же точность, если используем 20 или 30 наиболее важных показателей из 99, которые мы имели в наборе данных full_XY. При использовании показателя 1-MAE / Average мы теряем около 1% точности для 30 наиболее важных показателей и 2% точности для 20 наиболее важных показателей. Однако учтите, что любые коллинеарные точки могут заменить эти важные точки. Это очень важно понять. Если мы исключим самый важный столбец из этого графика, самый коллинеарный столбец займет его место и появится как новая самая важная точка. Как рассчитать все коллинеарные точки? К сожалению, лучшего пути я не знаю. Я обычно перебираю сотни моделей, чтобы найти все коллинеарные точки. Если бы было всего несколько столбцов, нам нужно было бы попробовать только дюжину комбинаций. Однако для этого набора данных у нас есть 99 столбцов для проверки всех комбинаций, а это очень много. Для линейной регрессии мы можем использовать многие меры, такие как VIF (коэффициент инфляции дисперсии), но для случайного леса он более эмпирический. Различные методы выполнения грубой силы описаны в https://towardsdatascience.com/explaining-feature-importance-by-example-of-a-random-forest-d9166011959e.

VII. Дополнительные ресурсы

Scikit-Learn RandomForestRegressor https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html

Введение в случайный лес https://towardsdatascience.com/understanding-random-forest-58381e0602d2

Подробнее о важности функции https://towardsdatascience.com/explaining-feature-importance-by-example-of-a-random-forest-d9166011959e