«Случайный лес» на Python (часть 1)

«Случайный лес» на Python (часть 1)

Nuances of programming

Перевод статьи William Koehrsen: Random Forest in Python

Практический пример машинного обучения

До сих по еще не было более удачного времени для машинного обучения. Благодаря доступным учебным онлайн ресурсам в Интернет, бесплатные инструменты с открытым исходным кодом, реализующие любые алгоритмы, который только можно себе вообразить, доступные и дешевые вычислительные мощности - доступные через облачные сервисы, такие как AWS, машинное обучение стало областью, демократизированной благодаря Интернету. Если есть ноутбук и желание учиться, можно легко опробовать любые современные алгоритмы за считанные минуты. Уже через весьма короткое время вы сможете разрабатывать практические модели, которые помогут вам в повседневной жизни или на работе (или даже перейти работать в сферу машинного обучения и начать зарабатывать деньги). В данном сообщении мы продемонстрируем вам полную реализацию модели машинного обучения на основе «случайного леса» ‑ комитета регрессионных деревьев принятия решений. Этот метод призван служить практическим дополнением описанной концепции «случайного леса», однако этот материал можно читать и самостоятельно при наличии представлений о самой идее дерева решений и «случайного леса». В дополнительном сообщении также подробно описывается, как можно улучшить предложенную здесь модель.

Конечно, здесь будет приводиться код на Python, однако досконального знания этого языка не потребуется, язык использован для демонстрации доступности машинного обучения с помощью распространенных сегодня ресурсов! Полный проект доступен на GitHub, а файл данных и Jupyter Notebook можно также загрузить с Google-диска. Все, что потребуется – ноутбук с установленным Python, запуск Jupyter Notebook, что позволит следовать за нашим дальнейшим описанием. (Для установки Python и запуска ноутбука Jupyter ознакомьтесь с руководством). Здесь будет несколько важных тем для понимания машинного обучения, но я постараюсь сделать их ясными и указать дополнительные ресурсы для более подробного изучения всеми заинтересовавшимися.

Введение в проблему

Будем заниматься задачей прогноза максимальной температуры на следующий день в городе на основе метеорологических данных за прошлый год. Поскольку я живу в Сиэтле, штат Вашингтон, то можете смело взять данные для своего города с помощью NOAA Climate Data Online. Будем действовать, как будто бы у нас нет доступа к прогнозам погоды (да и вообще, интереснее делать собственные прогнозы, а не полагаться на чужие). У нас имеются данные за год с максимальными значениями температур, значения температуры за два прошедших дня и оценки друга, который уверен, что знает о погоде все. Такая задача называется контролируемой регрессионной задачей машинного обучения. Она контролируемая, поскольку имеются как функции (данные для города), так и переменные (температура), для которых мы хотим построить прогноз. В процессе обучения мы подаем «случайный лес» и с характеристиками, и с целями, нейронная сеть должна научиться сопоставлять данные с прогнозом. Это регрессионная задача, поскольку целевое значение непрерывна(в отличие от дискретных классов классификации). Этого достаточно, поэтому давайте приступим!

План действий

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

  1. Поставить вопрос и определить требуемые данные
  2. Получить данные в доступном формате
  3. Доопределить / исправить отсутствующие точки данных / аномальные значения при необходимости
  4. Подготовить данные для модели машинного обучения
  5. Установите базовую модель
  6. Обучить модель на тренировочных данных
  7. Получить прогнозы по тренировочным данным
  8. Сравните прогнозы с известными целевыми наборами тестов и рассчитать коэффициент производительности
  9. Если производительность не удовлетворительна, отрегулировать модель, использовать больше данных или попробовать использовать другую технику моделирования
  10. Интерпретировать модель и представить результаты в визуальной и численной форме

Шаг 1 уже выполнен! Остался вопрос: «сможем ли мы предсказать максимальную температуру в городе завтра?» и помним, что нам, известны максимальные значения температуры за последний год в Сиэтле, штат Вашингтон.

Обработка данных

Во-первых, нам нужны некоторые данные. Чтобы пример был реален, я собрал данные о погоде для Сиэттла за 2016 год с помощью NOAA Climate Data Online. Как правило, около 80% времени, затрачиваемого на анализ данных, ‑ это сбор и очистка первичных данных, однако время на эти процедуры можно сократить с помощью использования высококачественных источников данных. Инструмент NOAA на удивление прост в использовании, и данные о температуре могут быть загружены в виде уже очищенных файлов csv, которые могут обрабатываться на таких языках, как Python или R. Полный файл данных доступен для загрузки всем, кто захочет повторить наши действия.

Следующий код Python загружает данные csv и отображает структуру данных:

# Pandas is used for data manipulation
import pandas as pd
# Read in data and display first 5 rows
features = pd.read_csv('temps.csv')
features.head(5)


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

Ниже приведены комментарии для столбцов:

год: 2016 для всех точек данных

месяц: число за месяц года

день: число за день года

неделя: день недели в виде символьной строки

temp_2: максимальная температура за 2 предыдущих дня

temp_1: максимальная температура за 1 предыдущий день

среднее: историческая средняя температура

фактическое: максимальная измеренная температура

друг: прогноз нашего друга – случайное число в диапазоне 20 градусов ниже средней 20 градусами выше средней температур

Определение аномальных значений / потеря данных

Если посмотреть на размерность данных, то можно заметить, что имеется только 348 строк, что не соответствует 366 дням в 2016 году. Просматривая данные из NOAA, я заметил несколько недостающих дней – это отличный пример того, что собранные реальные данные никогда не бывают идеальными. Отсутствующие данные, неверные данные или резкие отклонения в значениях могут повлиять на анализ. В нашем случае недостающие данные не окажут существенного влияния благодаря высокому качеству данных, взятых из надежного источника. Также можно заметить, что у нас имеется девять столбцов, представляющих собой восемь функций и одну цель («фактическую»).

print('The shape of our features is:', features.shape)
The shape of our features is: (348, 9)


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

# Descriptive statistics for each column
features.describe()


Сводные данные

У нас не нашлось такие точек, которые сразу проявили себя как аномальные, также отсутствуют столбцы с нулевыми значениями результатов измерений. Другим методом проверки качества данных являются основные графики. Часто легче заметить аномалии на графике, чем в цифрах. Я оставил здесь фактический код, потому что построение на Python не наглядно, но я советую вам самим попробовать на компьютере полную реализацию (например, как и любой хороший специалист по обработке и анализу данных, я довольно часто строю графики с помощью код из Stack Overflow).

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

Подготовка данных

К сожалению, мы пока еще не дошли до того момента, когда можно просто загрузить данные в модель и получить ответ (хотя над этим ведется работа)! Нам предстоит сделать небольшую модификацию, чтобы наши данные стали понятны машине. Будем использовать Python-библиотеку Pandas для обработки данных, основываясь на известную структуру dataframe, которая по-сути представляет собой таблицу Excel из строк и столбцов.

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

 Прямое кодирование

Первый наш шаг известен как прямое кодирование данных. Этот процесс принимает категориальные переменные (categorical variables) – дни недели, и преобразует их в числовое представление без произвольного упорядочения. Сокращенные названия дней недели для нас интуитивно. Наверняка, вы не найдете человека, не знающего, что «Пн» относится к первому дню рабочей недели, но машины не имеют интуитивных знаний. Компьютеры знают только цифры, и именно числа мы должны предоставить для машинного обучения. Можно было бы просто занумеровать дни недели числами с 1 по 7, но тогда алгоритм может придать большую важность значениям, приходящимся воскресенья, поскольку оно обозначено числом большей величины. Поэтому представим столбцы будних дней семью столбцами двоичных чисел. Это лучше проиллюстрировать наглядно. Прямое кодирование принимает следующий вид:

и принимает вид

Итак, если точкой данных является среда, в столбце среды будет стоять единица и нуль – во всех остальных столбцах. Этот процесс можно реализовать в pandas одной строкой!

# One-hot encode the data using pandas get_dummies
features = pd.get_dummies(features)
# Display the first 5 rows of the last 12 columns
features.iloc[:,5:].head(5)


Зафиксированные данные после прямого кодирования:

Данные после прямого кодирования

Форма наших данных теперь составляет 349 x 15, а все столбцы – это числа, что отлично подходит для алгоритма!

Возможности, цели и преобразование данных в массивы

Теперь нужно разделить данные на функции и цели. Цель, также называемая меткой, есть прогнозируемое значение, в нашем случае – фактическая максимальная температура, а функции – это все столбцы, используемые моделью для прогнозирования. Мы также преобразуем числовые дэйтафреймы Pandas в массивы Numpy, согласно работе алгоритма. (Я сохраняю заголовки столбцов, которые являются именами функций, списку, который будет использоваться для последующей визуализации).

# Use numpy to convert to arrays
import numpy as np
# Labels are the values we want to predict
labels = np.array(features['actual'])
# Remove the labels from the features
# axis 1 refers to the columns
features= features.drop('actual', axis = 1)
# Saving feature names for later use
feature_list = list(features.columns)
# Convert to numpy array
features = np.array(features)


Наборы данных для обучения и проверки

Теперь приступим к завершающему этапу подготовки исходных данных: разделение данных на обучающие и тестовые наборы. Во время обучения мы позволяем модели «видеть» ответы, в нашем случае – фактическую температуру, чтобы она могла узнать, как предсказать температуру на основе функций. Предполагаем, что имеется некоторая взаимосвязь между всеми функциями и целевым значением, и задача модели – изучить эти соотношения в процессе обучения. Затем, когда наступает момент оценки модели, мы просим ее сделать прогноз на тестовом наборе данных, в этом случае предоставлен доступ только к функциям (но не к ответам!). Поскольку у нас есть ответы для набора тестов, мы можем сравнить полученные прогнозы с истинными значениями, чтобы оценить точность модели. Как правило, при обучении модели мы произвольно разбиваем данные на обучающие и тестовые наборы, чтобы получить представление всех точек данных (например, если мы обучали модель на данных первых девяти месяцев года, а затем использовали данные последних трех месяцев для прогнозирования, то наш алгоритм не сможет хорошо сработать, потому что он не обучался на данных последних трех месяцев). Я установил случайное значение равным 42, что означает, что результаты будут одинаковыми при каждом запуске разбиения для получения воспроизводимых результатов.

Нижеследующий код разбивает набор данных в еще одну строку:

# Using Skicit-learn to split data into training and testing sets
from sklearn.model_selection import train_test_split
# Split the data into training and testing sets
train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size = 0.25, random_state = 42)


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

print('Training Features Shape:', train_features.shape)
print('Training Labels Shape:', train_labels.shape)
print('Testing Features Shape:', test_features.shape)
print('Testing Labels Shape:', test_labels.shape)
Training Features Shape: (261, 14)
Training Labels Shape: (261,)
Testing Features Shape: (87, 14)
Testing Labels Shape: (87,)


Пока все в порядке! Напомним, чтобы получить данные в форме, приемлемой для машинного обучения, мы:

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

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

Организация основных данных

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

# The baseline predictions are the historical averages
baseline_preds = test_features[:, feature_list.index('average')]
# Baseline errors, and display average baseline error
baseline_errors = abs(baseline_preds - test_labels)
print('Average baseline error: ', round(np.mean(baseline_errors), 2))
Average baseline error:  5.06 degrees.


У теперь у нас есть цель! Если не сможем превзойти среднюю ошибку в 5 градусов, то придется пересмотреть подход в целом.

Тренировка модели

После проведения работы по подготовке данных, создание и обучение модели довольно просто осуществить с помощью Scikit-learn. Импортируем модель случайного регрессионного леса из skicit-learn, создаем экземпляр модели и тренируем (имя scikit-learn для обучения) модель на наборе данных для обучения. (Снова устанавливаем случайное состояние для воспроизводимых результатов). Процесс описывается тремя строками в scikit-learn!

# Import the model we are using
from sklearn.ensemble import RandomForestRegressor
# Instantiate model with 1000 decision trees
rf = RandomForestRegressor(n_estimators = 1000, random_state = 42)
# Train the model on training data
rf.fit(train_features, train_labels);


Получение прогнозов на тестовом наборе

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

Выполнение прогнозов с использованием модели – это еще одна однострочная команда в Skicit-learn.

# Use the forest's predict method on the test data
predictions = rf.predict(test_features)
# Calculate the absolute errors
errors = abs(predictions - test_labels)
# Print out the mean absolute error (mae)
print('Mean Absolute Error:', round(np.mean(errors), 2), 'degrees.')
Mean Absolute Error: 3.83 degrees.


Наша средняя оценка отклоняется на 3,83 градуса. Это в среднем на один градус больше по сравнению с исходным уровнем. Хотя это может показаться незначительным, но это на 25% лучше базового уровня, который, в зависимости от области и проблемы, может представлять собой, например, для крупной компании миллионы долларов.

Определение показателей эффективности

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

# Calculate mean absolute percentage error (MAPE)
mape = 100 * (errors / test_labels)
# Calculate and display accuracy
accuracy = 100 - np.mean(mape)
print('Accuracy:', round(accuracy, 2), '%.')
Accuracy: 93.99 %.


Выглядит неплохо! Наша модель научилась прогнозировать максимальную температуру в Сиэтле на следующий день с точностью в 94%.

Конец части 1. Продолжение следует...

Статью перевел Владислав Семёнов.

Report Page