• Установите необходимые библиотеки
  • Скачать данные с 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 $500
  • longitudes: -75 to -72
  • latitudes: 40 to 42
  • passenger_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)

Рекомендации