Разработка функций - это искусство понимания бизнес-проблемы и использования знаний предметной области для выбора, создания и преобразования переменных, которые будут введены в вашу модель машинного обучения. Этот шаг очень важен в прикладном машинном обучении, поскольку качество и количество функций будут иметь большое влияние на то, хороша модель или нет.
Соответствующие хорошо продуманные функции часто являются решающим фактором производительности вашего окончательного алгоритма. По этой причине специалисты по обработке данных часто тратят 70% -80% своего времени на этап предварительного моделирования, который в основном состоит из проектирования функций.
Давайте углубимся и попробуем поработать на реальном примере…
Данные, которые мы будем использовать, взяты из выигранного мной конкурса.
Мы попытаемся построить модель, которая прогнозирует количество мест, которое Mobiticket может рассчитывать на продавать за каждую поездку. В этом наборе данных 14 маршрутов. Все маршруты заканчиваются в Найроби и берут начало в городах к северо-западу от Найроби в сторону озера Виктория.
Описание переменных:
- ride_id: уникальный идентификатор транспортного средства на определенном маршруте в определенный день и время.
- seat_number: место, назначенное билету
- payment_method: метод, используемый клиентом для покупки билета в Mobiticket (наличными или Mpesa)
- payment_receipt: уникальный идентификационный номер для билета, приобретенного в Mobiticket
- travel_date: дата отправления рейса. (ММ / ДД / ГГГГ)
- travel_time: время отправления по расписанию. Поездки обычно отправляются вовремя. (чч: мм)
- travel_from: город, из которого началась поездка
- travel_to: пункт назначения. Все поездки в Найроби.
- car_type: тип транспортного средства (шаттл или автобус)
- max_capacity: количество мест в транспортном средстве
Первым делом мы импортируем библиотеки, которые собираемся использовать.
import pandas as pd #to use dataframes import numpy as np #for linear algebra operations import matplotlib.pyplot as plt #for visualization import seaborn as sns from sklearn.model_selection import train_test_split,KFold import xgboost as xgb #the model we're going to use from sklearn.metrics import mean_absolute_error #our metric
Теперь нам нужно загрузить наш поезд и тестовые данные.
train=pd.read_csv(“./Data/train.zip”) test=pd.read_csv(“./Data/test.csv”)
К сожалению, данные не содержат количество билетов на поездку, поэтому нам нужно собрать данные и попытаться сгруппировать их по ride_id, чтобы найти нужное количество билетов на поездку.
Target=train.groupby([“ride_id”]).seat_number.count().rename(“number_of_ticket”).reset_index() train=train.drop_duplicates(“ride_id”).drop([‘payment_method’, ‘payment_receipt’, ‘seat_number’],axis=1) train=train.merge(Target,how=”left”,on=”ride_id”) train.drop([“travel_to”],axis=1,inplace=True) test.drop([“travel_to”],axis=1,inplace=True)
Теперь мы должны обработать столбцы данных и добавить временные характеристики, которые очень важны при работе с проблемами временных рядов.
def add_20(x): date=x.split(“-”) date[-1]=”20"+date[-1] return “-”.join(date) train[“date”]=(train[“travel_date”].apply(add_20)+” “+train[“travel_time”]).astype(str) test[“date”]=(test[“travel_date”]+” “+test[“travel_time”]).astype(str)
Мы создадим функцию time_features, которая будет извлекать все временные данные из заданных данных, таких как час, день, месяц, выходные или нет ...
def time_features(x): x[“date”]=pd.to_datetime(x[“date”],format=’%d-%m-%Y %H:%M’) x[“dayofweek”]=x[“date”].dt.dayofweek x[“dayofyear”]=x[“date”].dt.dayofyear x[“dayofmonth”]=x[“date”].dt.day x[“year_woy”]=x[“date”].dt.year.astype(str)+x[“date”].dt.weekofyear.astype(str) x[“hour”]=x[“date”].dt.hour x[“minute”]=x[“date”].dt.minute x[“is_weekend”]=x[“dayofweek”].apply( lambda x : 1 if x in [5,6] else 0 ) x[“year”]=x[“date”].dt.year x[“quarter”]=x[“date”].dt.quarter x[“month”]=x[“date”].dt.month return x train=time_features(train) test=time_features(test)
Визуализация
Пришло время визуализировать наши данные. Для этого мы сосредоточимся на категориальных функциях и построим функцию для построения графика подсчета и среднего числа билетов, сгруппированных по выбранной функции.
categorical_feautres=[“travel_from”,”car_type”,”dayofweek”,”dayofmonth”,”hour”,”minute”,”year”] def plot_categorical_feature(feautre): fig, axes = plt.subplots(nrows=1, ncols=2) train.groupby(feautre).number_of_ticket.mean()\ .plot(kind=”bar”,ax=axes[0],figsize=(20,5),title=”mean of number_of_ticket per {}”.format(feautre)) train.groupby(feautre).number_of_ticket.count().\ plot(kind=”bar”,ax=axes[1],figsize=(20,5),title=”ride count per {}”.format(feautre)) plt.show() for feautre in categorical_feautres : plot_categorical_feature(feautre)
Целевое кодирование
Теперь мы определим функцию, которая принимает фрейм данных, имя категориальной функции, имя целевого столбца и статистические функции в качестве входных данных и возвращает совокупный фрейм данных, сгруппированный по категориальной функции.
def target_encoding(data,feautre, target=”number_of_ticket”, agg_functions={“mean”,”std”}): agg=data.groupby(feautre)[target].agg(agg_functions) agg.columns=[column+”_per_{}_{}”.format(feautre,target) for column in agg.columns.tolist()] return agg
В этом руководстве мы будем использовать три подхода к кодированию.
Итак, мы определим еще одну вспомогательную функцию для вычисления совокупного фрейма данных для каждой категориальной функции и объединения их с обучающими и тестовыми наборами.
Эта функция будет использоваться как наш первый подход к целевой кодировке:
def add_target_encoding_features(train,test): for feautre in categorical_feautres : agg=target_encoding(train,feautre) train=train.merge(agg,how=”left”,on=feautre) test=test.merge(agg,how=”left”,on=feautre) return train,test train_TE,test_TE=add_target_encoding_features(train_TE,test_TE)
Во многих случаях, когда вы используете весь набор поездов для расчета совокупных характеристик, вы получите хороший результат для набора для обучения и проверки и плохой результат для набора тестов (переобучение) из-за сильной корреляции между новыми сгенерированными функциями. и мишень в поезде. Чтобы избежать этой проблемы, мы должны с самого начала разделить данные поезда на новые наборы поездов и проверки и рассчитать совокупные характеристики, используя только новый поезд. Мы присоединимся к нему позже с набором проверки и набором тестов. Затем мы сравниваем оценку обучения и проверки, чтобы узнать, переоснащаются ли агрегированные характеристики или нет.
Второй подход будет выполнен с этой функцией. Сначала нам нужно разделить наши данные поезда на 2 части: данные поезда (поезд поезда lol, но нам нужен набор валидации из набора данных большого поезда для оценки нашей модели) и данные валидации.
train_VTE,val_VTE=train_test_split(train,random_state=1994,test_size=0.1) test_VTE=test.copy() def add_target_encoding_features_validation(train,val,test): for feautre in categorical_feautres : agg=target_encoding(train,feautre) train=train.merge(agg,how="left",on=feautre) val=val.merge(agg,how="left",on=feautre) test=test.merge(agg,how="left",on=feautre) return train,val,test train_VTE,val_VTE,test_VTE=add_target_encoding_features_validation(train_VTE,val_VTE,test_VTE)
Этот последний метод выполняется с использованием kfold вместо простого разделения.
def add_target_encoding_features_Kfold(train,test,split=10): kf = KFold(n_splits=split,random_state=2222,shuffle=False) train_final=[] for train_index, test_index in kf.split(train): train_fold, val_fold = train.loc[train_index], train.loc[test_index] for feautres in categorical_feautres: agg=target_encoding(train_fold,feautres) val_fold=val_fold.merge(agg,how="left",on=feautres) train_final.append(val_fold) for feautres in categorical_feautres : agg=target_encoding(train,feautres) test=test.merge(agg,how="left",on=feautres) return pd.concat(train_final),test train_KTE=train.copy() test_KTE=test.copy() train_KTE,test_KTE=add_target_encoding_features_Kfold(train=train_KTE,test=test_KTE)
Моделирование
Теперь все готово. Нам просто нужно определить нашу модель и обучить ее. Для этого мы собираемся определить функцию для обучения данных в одном направлении, которая принимает данные обучения, проверки и тестирования и возвращает прогноз поезда, проверки и тестирования.
def train_model(X_train,Y_train,X_val, Y_val,X_test,parmaters,features_name): d_train = xgb.DMatrix(X_train, Y_train,feature_names=features_name) d_valid = xgb.DMatrix(X_val, Y_val,feature_names=features_name) d_test = xgb.DMatrix(X_test,feature_names=features_name) list_track = [(d_train, 'train'), (d_valid, 'valid')] model = xgb.train(parmaters, d_train, 2000, list_track, maximize=False, verbose_eval=50, early_stopping_rounds=50) train_pred =model.predict(d_train) valid_pred =model.predict(d_valid) test_pred = model.predict(d_test) return train_pred ,valid_pred,test_pred
Поскольку мы хотим быть более уверенными в производительности нашего алгоритма. Мы создаем пять различных моделей, используя наш алгоритм обучения, и тестируем его на пяти различных наборах тестов. Этот метод реализован в sklearn в функции Kfold из sklearn.model_selection.
def train_kfold(X_train,Y_train,X_test, parmaters,features_name,split=5,): final_train_pred=np.zeros_like(Y_train) final_test_pred=np.zeros(len(X_test)) kf = KFold(n_splits=split,random_state=2222) i=1 for train_index, val_index in kf.split(X_train): print("fold:"+str(i)) train_fold_features, val_fold_features = X_train.loc[train_index], X_train.loc[val_index] train_fold_target, val_fold_target = Y_train.loc[train_index], Y_train.loc[val_index] train_pred ,valid_pred,test_pred=train_model( X_train=train_fold_features, Y_train= train_fold_target, X_val= val_fold_features, Y_val= val_fold_target, X_test= X_test, parmaters=parmaters, features_name=features_name ) final_train_pred[val_index]=valid_pred final_test_pred=final_test_pred+test_pred/split i=i+1 return final_train_pred,final_train_pred
Теперь нам нужно удалить бесполезные функции.
columns_to_remove=['ride_id', 'travel_date', 'travel_time', 'travel_from', 'car_type', 'max_capacity', 'number_of_ticket', 'date', 'dayofweek', 'dayofyear', 'dayofmonth', 'year_woy', 'hour', 'minute', 'year', 'quarter', 'month'] features_name=train_KTE.drop(columns_to_remove,axis=1).columns
Обучение
train_TE_train,train_TE_val=train_test_split(train_TE,random_state=1994,test_size=0.1) X_train=train_TE_train[features_name] Y_train=train_TE_train["number_of_ticket"] X_val=train_TE_val[features_name] Y_val=train_TE_val["number_of_ticket"] X_test=test_TE[features_name] params = {'eta': 0.004, 'colsample_bytree': 0.7, 'max_depth': 9,'subsample': 0.9, 'lambda': 4, 'nthread': 8, 'booster' : 'gbtree', 'silent': 1, 'eval_metric': 'rmse', 'objective': 'reg:linear', "gamma":0.1 ,"alpha":0.01} train_pred ,valid_pred,test_pred=train_model(X_train, Y_train,X_val, Y_val,X_test, params,features_name)
Ссылка на Github