- Установите необходимые библиотеки
- Скачать данные с Kaggle
- Просмотр файлов набора данных
- Загрузите тренировочный набор с Pandas
- Загрузить тестовый набор с Pandas
Установка необходимых библиотек
!pip install numpy pandas jovian opendatasets scikit-learn xgboost --quiet
Приступаем к кодированию!!
import opendatasets url="https://www.kaggle.com/c/new-york-city-taxi-fare-prediction" opendatasets.download(url) dataDir="new-york-city-taxi-fare-prediction" import pandas as p col="fare_amount,pickup_datetime,pickup_longitude,pickup_latitude,dropoff_longitude,dropoff_latitude,passenger_count".split(",") random.seed(99) def skips(ind): if ind==0: return False return random.random()>0.01
Работа с 1% выборкой данных (~500 тыс. строк)
df = pd.read_csv(data_dir+"/train.csv", usecols=selected_cols, dtype=dtypes, parse_dates=['pickup_datetime'], skiprows=skip_row)
Мы считываем набор данных в DataFrame df и смотрим на shape , columns , типы данных столбцов. и первые 5 строк данных. Это даст краткий обзор имеющихся данных.
Описание столбца
- pickup_datetime — дата и время включения счетчика.
- dropoff_datetime — дата и время отключения счетчика.
- passenger count — количество пассажиров в транспортном средстве (значение, введенное водителем).
- Pickup Longitude – долгота, на которой был задействован счетчик.
- широта захвата – широта, на которой был задействован счетчик.
- dropoff_longitude – долгота, на которой счетчик был отключен.
- dropoff_latitude — широта, на которой счетчик был отключен.
- fare_amount — (целевая) общая стоимость поездки.
df.shape
df.head()
df.describe()
df.pickup_datetime.min(),df.pickup_datetime.max()
Наблюдения по поводу данных обучения:
- 550 тыс.+ строк, как и ожидалось
- Нет недостающих данных (в образце)
fare_amount
находится в диапазоне от −52,0 до −52,0 до 499,0.passenger_count
находится в диапазоне от 0 до 208- Кажется, есть некоторые ошибки в значениях широты и долготы
- Диапазон дат: с 1 января 2009 года по 30 июня 2015 года.
- Набор данных занимает ~19 МБ места в оперативной памяти.
Возможно, нам придется иметь дело с выбросами и ошибками ввода данных, прежде чем мы будем обучать нашу модель.
Набор тестов
test_df.describe()
test_df.pickup_datetime.min(), test_df.pickup_datetime.max()
Несколько замечаний по тестовому набору:
- 9914 строк данных
- Нет пропущенных значений
- Нет явных ошибок при вводе данных
- От 1 до 6 пассажиров (мы можем ограничить тренировочные данные этим диапазоном)
- Широта лежит между 40 и 42
- Долготы лежат между -75 и -72.
- Даты получения варьируются с 1 января 2009 г. по 30 июня 2015 г. (как и в тренировочном наборе).
Подготовьте набор данных для обучения
Раздельное обучение и набор для проверки
Мы выделим 20% обучающих данных в качестве проверочного набора, чтобы оценить модели, которые мы обучаем на ранее невиданных данных.
Поскольку диапазоны дат для тестовой и обучающей выборки совпадают, мы можем выбрать случайную долю в 20 %.
train_df, val_df = train_test_split(df, test_size=0.2, random_state=42) len(train_df), len(val_df)
Заполнить/удалить пропущенные значения
В нашей выборке нет пропущенных значений, но если бы они были, мы могли бы просто отбрасывать строки с пропущенными значениями вместо того, чтобы пытаться их заполнить (поскольку у нас много обучающих данных).
train_df = train_df.dropna() val_df = val_df.dropna() input_cols = ['pickup_longitude', 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude', 'passenger_count'] target_col = 'fare_amount'
Обучение
train_inputs = train_df[input_cols] train_targets = train_df[target_col]
Проверка
val_inputs = val_df[input_cols] val_targets = val_df[target_col]
Обучите и оцените жестко закодированную модель
import numpy as np class MeanRegressor(): def fit(self, inputs, targets): self.mean = targets.mean() def predict(self, inputs): return np.full(inputs.shape[0], self.mean) mean_model = MeanRegressor() mean_model.fit(train_inputs, train_targets) mean_model.mean
train_preds = mean_model.predict(train_inputs) train_preds
val_preds = mean_model.predict(val_inputs)
from sklearn.metrics import mean_squared_error train_rmse = mean_squared_error(train_targets, train_preds, squared=False)
rms(valTarget,meanModel.predict(valInputs))
Наша тупая жестко запрограммированная модель отличается в среднем на 9,899, что довольно плохо, учитывая, что средний тариф составляет в среднем 9,899, что довольно плохо, учитывая, что средний тариф составляет 11,35.
Обучите и оцените базовую модель
Мы будем обучать модель линейной регрессии в качестве нашей базовой линии, которая пытается выразить цель как взвешенную сумму входных данных. Да, это все.
class MeanRegressor(): def fit(self, inputs, targets): self.mean = targets.mean() def predict(self, inputs): return np.full(inputs.shape[0], self.mean) from sklearn.linear_model import LinearRegression linreg_model = LinearRegression() linreg_model.fit(train_inputs, train_targets) train_preds = linreg_model.predict(train_inputs) val_preds = linreg_model.predict(val_inputs) train_rmse = mean_squared_error(train_targets, train_preds, squared=False) train_rmse
val_rmse = mean_squared_error(val_targets, val_preds, squared=False) val_rmse
Разработка функций
- Извлечь части даты
- Удалить выбросы и неверные данные
- Добавьте расстояние между подъемом и падением
- Добавить расстояние от ориентиров
Упражнение: мы собираемся применить все вышеперечисленное вместе, но вы должны наблюдать за эффектом добавления каждой функции по отдельности.
### Extract Parts of Date - Year - Month - Day - Weekday - Hour def add_dateparts(df, col): df[col + '_year'] = df[col].dt.year df[col + '_month'] = df[col].dt.month df[col + '_day'] = df[col].dt.day df[col + '_weekday'] = df[col].dt.weekday df[col + '_hour'] = df[col].dt.hour add_dateparts(train_df, 'pickup_datetime') add_dateparts(val_df, 'pickup_datetime') add_dateparts(test_df, 'pickup_datetime')
Добавить расстояние между подъемом и падением
import numpy as np def haversine_np(lon1, lat1, lon2, lat2): """ Calculate the great circle distance between two points on the earth (specified in decimal degrees) All args must be of equal length. """ lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) dlon = lon2 - lon1 dlat = lat2 - lat1 a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2 c = 2 * np.arcsin(np.sqrt(a)) km = 6367 * c return km
Добавить расстояние от популярных достопримечательностей
- аэропорт имени Джона Кеннеди
- Аэропорт ЛГА
- ЭВР аэропорт
- Таймс Сквер
- Встретил Меузеум
- Всемирный торговый центр
Мы добавим расстояние от места падения.
jfk_lonlat = -73.7781, 40.6413 lga_lonlat = -73.8740, 40.7769 ewr_lonlat = -74.1745, 40.6895 met_lonlat = -73.9632, 40.7794 wtc_lonlat = -74.0099, 40.7126 def add_landmark_dropoff_distance(df, landmark_name, landmark_lonlat): lon, lat = landmark_lonlat df[landmark_name + '_drop_distance'] = haversine_np(lon, lat, df['dropoff_longitude'], df['dropoff_latitude']) %%time for a_df in [train_df, val_df, test_df]: for name, lonlat in [('jfk', jfk_lonlat), ('lga', lga_lonlat), ('ewr', ewr_lonlat), ('met', met_lonlat), ('wtc', wtc_lonlat)]: add_landmark_dropoff_distance(a_df, name, lonlat)
Удалить выбросы и неверные данные
Кажется, в каждом из следующих столбцов есть недействительные данные:
- Сумма тарифа
- Количество пассажиров
- Широта и долгота пикапа
- Отбросить широту и долготу
Мы будем использовать следующие диапазоны:
fare_amount
: $1 to $500longitudes
: -75 to -72latitudes
: 40 to 42passenger_count
: 1 to 6
def remove_outliers(df): return df[(df['fare_amount'] >= 1.) & (df['fare_amount'] <= 500.) & (df['pickup_longitude'] >= -75) & (df['pickup_longitude'] <= -72) & (df['dropoff_longitude'] >= -75) & (df['dropoff_longitude'] <= -72) & (df['pickup_latitude'] >= 40) & (df['pickup_latitude'] <= 42) & (df['dropoff_latitude'] >=40) & (df['dropoff_latitude'] <= 42) & (df['passenger_count'] >= 1) & (df['passenger_count'] <= 6)] train_df = remove_outliers(train_df) val_df = remove_outliers(val_df)
Масштабирование и горячее кодирование
Упражнение. Попробуйте масштабировать числовые столбцы до диапазона (0,1)
и закодировать категориальные столбцы с помощью одноразового кодировщика.
Мы не будем этого делать, потому что мы будем обучать древовидные модели, которые, как правило, способны хорошо работать даже без вышеперечисленного.
Сохранить промежуточные кадры данных
Давайте сохраним обработанные наборы данных в формате Apache Parquet, чтобы мы могли легко загрузить их обратно, чтобы продолжить нашу работу с этого момента.
Вы также можете создать разные блокноты для EDA, разработки функций и обучения моделей.
train_df.to_parquet('train.parquet') val_df.to_parquet('val.parquet') test_df.to_parquet('test.parquet')
Обучайте и оценивайте различные модели
Мы обучим каждое из следующих действий и отправим прогнозы в Kaggle:
- Линейная регрессия
- Случайные леса
- Повышение градиента
Упражнение: Train Ridge, SVM, KNN, модели дерева решений
Разделить входы и цели
input_cols = ['pickup_longitude', 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude', 'passenger_count', 'pickup_datetime_year', 'pickup_datetime_month', 'pickup_datetime_day', 'pickup_datetime_weekday', 'pickup_datetime_hour', 'trip_distance', 'jfk_drop_distance', 'lga_drop_distance', 'ewr_drop_distance', 'met_drop_distance', 'wtc_drop_distance'] target_col = 'fare_amount' train_inputs = train_df[input_cols] train_targets = train_df[target_col] val_inputs = val_df[input_cols] val_targets = val_df[target_col] test_inputs = test_df[input_cols] def evaluate(model): train_preds = model.predict(train_inputs) train_rmse = mean_squared_error(train_targets, train_preds, squared=False) val_preds = model.predict(val_inputs) val_rmse = mean_squared_error(val_targets, val_preds, squared=False) return train_rmse, val_rmse, train_preds, val_preds def predict_and_submit(model, fname): test_preds = model.predict(test_inputs) sub_df = pd.read_csv(data_dir+'/sample_submission.csv') sub_df['fare_amount'] = test_preds sub_df.to_csv(fname, index=None) return sub_df
Ридж-регрессия
from sklearn.linear_model import Ridge model1 = Ridge(random_state=42) %%time model1.fit(train_inputs, train_targets) evaluate(model1)
Случайный лес
from sklearn.ensemble import RandomForestRegressor model2 = RandomForestRegressor(max_depth=10, n_jobs=-1, random_state=42, n_estimators=50) %%time model2.fit(train_inputs, train_targets)
Повышение градиента
from xgboost import XGBRegressor model3 = XGBRegressor(random_state=42, n_jobs=-1, objective='reg:squarederror') %%time model3.fit(train_inputs, train_targets) %%time model3.fit(train_inputs, train_targets)
evaluate(model3)
Настройка гиперпараметров
Мы будем обучать параметры для модели XGBoost. Вот стратегия настройки гиперпараметров:
- Сначала настройте наиболее важный/важный гиперпараметр, например. n_оценщиков
- С лучшим значением первого гиперпараметра настройте следующий наиболее важный гиперпараметр.
- И так далее, продолжайте тренировать следующие наиболее важные параметры с лучшими значениями предыдущих параметров…
- Затем вернитесь к началу и снова настройте каждый параметр для получения дополнительной предельной выгоды.
- К сожалению, настройка гиперпараметров — это больше искусство, чем наука. Попытайтесь почувствовать, как параметры взаимодействуют друг с другом, исходя из вашего понимания параметра…
Давайте определим вспомогательную функцию для проверки различных гиперпараметров.
import matplotlib.pyplot as plt def test_params(ModelClass, **params): """Trains a model with the given parameters and returns training & validation RMSE""" model = ModelClass(**params).fit(train_inputs, train_targets) train_rmse = mean_squared_error(model.predict(train_inputs), train_targets, squared=False) val_rmse = mean_squared_error(model.predict(val_inputs), val_targets, squared=False) return train_rmse, val_rmse def test_param_and_plot(ModelClass, param_name, param_values, **other_params): """Trains multiple models by varying the value of param_name according to param_values""" train_errors, val_errors = [], [] for value in param_values: params = dict(other_params) params[param_name] = value train_rmse, val_rmse = test_params(ModelClass, **params) train_errors.append(train_rmse) val_errors.append(val_rmse) plt.figure(figsize=(10,6)) plt.title('Overfitting curve: ' + param_name) plt.plot(param_values, train_errors, 'b-o') plt.plot(param_values, val_errors, 'r-o') plt.xlabel(param_name) plt.ylabel('RMSE') plt.legend(['Training', 'Validation']) best_params = { 'random_state': 42, 'n_jobs': -1, 'objective': 'reg:squarederror' }
Количество деревьев
%%time test_param_and_plot(XGBRegressor, 'n_estimators', [100, 250, 500], **best_params)
best_params['n_estimators'] = 250
Максимальная глубина
%%time test_param_and_plot(XGBRegressor, 'max_depth', [3, 4, 5], **best_params)
Скорость обучения
%%time test_param_and_plot(XGBRegressor, 'learning_rate', [0.05, 0.1, 0.25], **best_params)
best_params['max_depth'] = 5
Другие параметры
Точно так же мы можем поэкспериментировать с другими параметрами. Вот набор параметров, который хорошо работает:
xgb_model_final = XGBRegressor(objective='reg:squarederror', n_jobs=-1, random_state=42, n_estimators=500, max_depth=5, learning_rate=0.1, subsample=0.8, colsample_bytree=0.8) %%time xgb_model_final.fit(train_inputs, train_targets)
evaluate(xgb_model_final)
Рекомендации
- Набор данных: https://www.kaggle.com/c/new-york-city-taxi-fare-prediction/overview
- Отсутствующий семестр (скрипты оболочки): https://missing.csail.mit.edu/
- Библиотека Opendatsets: https://github.com/JovianML/opendatasets
- Проект EDA с нуля: https://www.youtube.com/watch?v=kLDTbavcmd0
- GeoPy: https://geopy.readthedocs.io/en/stable/#module-geopy.distance
- Сообщение в блоге Аллена Конга: https://towardsdatascience.com/nyc-taxi-fare-prediction-605159aa9c24
- Машинное обучение с Python: от нуля до GBM — https://zerotogbms.com
- Таблица отслеживания экспериментов: https://bit.ly/mltrackingsheet
- Компоненты даты и времени Pandas: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#time-date-components
- Хаверсинусное расстояние: https://en.wikipedia.org/wiki/Haversine_formula
- Расстояние гаверсина с Numpy: https://stackoverflow.com/questions/29545704/fast-haversine-approimation-python-pandas
- RAPIDS (родительский проект для cudf и cuml): https://rapids.ai/
- Сообщение в блоге Data Science с нуля: https://www.youtube.com/watch?v=NK6UYg3-Bxs
- Примеры проектов машинного обучения:
- Продажи в магазине Walmart: https://jovian.ai/anushree-k/final-walmart-simple-rf-gbm
- Прогноз цен на подержанные автомобили: https://jovian.ai/kara-mounir/used-cars-prices
- Литологический прогноз: https://jovian.ai/ramysaleem/ml-project-machine-predicting-lithologies
- Прогноз спроса на рекламу: https://jovian.ai/deepa-sarojam/online-ad-demand-prediction-ml-prj