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

Вместо обсуждения того, как я подготовил данный набор данных (очистка данных, споры, вменение), которые вы можете найти в проекте репозиторий под data_preprocess.py, я сразу перейду к забавным вещам. Я решил использовать алгоритмы, использующие деревья решений, потому что они легко интерпретируются, способны обрабатывать категориальные и числовые данные (каждого из которых у нас много), очень точны и чертовски красивы (они так сказали). Созданы модели:

import pandas as pd
from sklearn.model_selection import train_test_split

train_knn_df = pd.read_csv('wrangled_data/train_knn_df.csv')
X = train_knn_df.drop(columns='SalePrice')
y = train_knn_df['SalePrice']
X_train, X_test, y_train, y_test = train_test_split(X,y, random_state=33)

#_______________________________________GradientBooster_______________________________________
from sklearn.ensemble import GradientBoostingRegressor

gbr = GradientBoostingRegressor(random_state=0).fit(X_train, y_train)
gbr.score(X_test, y_test)

#_______________________________________AdaBoost_______________________________________
from sklearn.ensemble import AdaBoostRegressor

adabr = AdaBoostRegressor(random_state=0).fit(X_train, y_train)
adabr.score(X_test, y_test)
#_______________________________________DecisionTrees_______________________________________
from sklearn.tree import DecisionTreeRegressor

dtc = DecisionTreeRegressor(random_state=0).fit(X_train, y_train)
dtc.score(X_test,y_test)

#_______________________________________RandomForest_______________________________________
from sklearn.ensemble import RandomForestRegressor

rfr = RandomForestRegressor(random_state=0).fit(X_train, y_train)
rfr.score(X_test, y_test)

RandomForest — это алгоритм, который создает ансамбль DecisionTrees и повышает точность за счет усреднения ансамбля. Повышение, используемое AdaBoost и GradientBoosting, как показано выше, — это метод создания ансамбля, в котором каждая новая модель, добавляемая в ансамбль, учится на ошибках (неточностях) предыдущей. Каждая новая модель усилена всеми предыдущими.

Первоначальная модель GradientBoosterRegression дала точность обучения (R-квадрат) 89%, в то время как модели RandomForest, DecisionTree и AdaBoost были между 78% и 84%. Это неплохая оценка, но я не собирался соглашаться, пока не убедился, что моя модель — лучшая версия самой себя. Здесь в игру вступает тюнинг модели. Популярным методом настройки алгоритма является использование GridSearchCV, который должен быть инструментом в наборе инструментов любого инженера по машинному обучению. Данные могут быть странными и случайными, поэтому для создания наилучшей модели крайне важно, чтобы можно было быстро и легко создать множество моделей с множеством различных параметров. Термин лучшая модель является субъективным и определяется по-разному в зависимости от организации. Тем не менее, всегда следует помнить о регуляризации, обобщении, предвзятости и минимизации переобучения (эта статья объясняет эти термины ML). Вот как я использовал поиск по сетке:

from sklearn.model_selection import GridSearchCV
X_actual = train_knn_df.drop(columns='SalePrice')
y_actual = train_knn_df['SalePrice'] 
X_train, X_test, y_train, y_test = train_test_split(X_actual, y_actual, random_state=random_state, test_size=0.35)

tune_params = {'learning_rate':[0.01, 0.1,  1.0], 
               'n_estimators' : [500,1000,2000], 
               'loss' : ['ls', 'lad', 'huber', 'quantile'],
               'min_samples_split' : [2,4], 
               'min_samples_leaf' : [1,2]
               }
GradientBoostR = GradientBoostingRegressor(random_state = random_state)

model_selection_ = GridSearchCV(GradientBoostR, tune_params, verbose=1, n_jobs=2)
model_selection_.fit(X_train, y_train)

models_df = pd.DataFrame.from_dict(model_selection_.cv_results_).sort_values(by='mean_test_score', ascending=False)

GridSearch может занять много времени, потому что он должен создать много моделей. Например, я указал 3 параметра скорости обучения, 3 оценщика, 4 функции потерь, 2 расщепления и лист с 2-минутными выборками, всего 720 моделей. (3*3*4*2*2 * 5 (по умолчанию cv сворачивается). Чтобы ускорить этот процесс, вы можете указать n_jobs равным 2 или даже использовать HalvingGridSearch, который работает до 20 раз быстрее и является отличным аппроксиматором. Результаты показывают что наилучший набор параметров - это скорость обучения 0,1, количество оценок / деревьев 1000, huber в качестве функции потерь и 2 для минимального разделения выборок и минимальных выборок листьев, Таким образом, строится окончательная модель, и ее точность интерпретируется с использованием перекрестной проверки.

#_______________________________________FINAL MODEL_______________________________________
optim_params = {'learning_rate': 0.05, 
                'n_estimators' : 1000, 
                'loss' : 'huber',
                'min_samples_split' : 2, 
                'min_samples_leaf' : 2, 
                'random_state':random_state}
GradientBoostR = GradientBoostingRegressor(**optim_params)
GradientBoostR.fit(X_train, y_train)

#_______________________________________MODEL INTERPRETAION_______________________________________
# overfitting vs underfitting 
from sklearn.model_selection import cross_validate

cv_results = cross_validate(GradientBoostR, X_actual, y_actual, cv=3, return_train_score=True, return_estimator=True, scoring=('r2'))
print("TEST score   : {:.1%}".format(np.mean(cv_results['test_score'])) , " (+/-) {:.1%}".format(np.std(cv_results['test_score'])))
print("TRAIN score  : {:.1%}".format(np.mean(cv_results['train_score'])), " (+/-) {:.1%}".format(np.std(cv_results['train_score'])))

Модель имеет точность обучения 99,0% и точность тестирования 88,1%. Значительное улучшение по сравнению с первоначальной моделью, которая была создана. Взглянув на предсказанную моделью цену продажи по сравнению с фактической ценой продажи, хотя есть несколько точек, в которых модель промахнулась (самые большие ошибки), модель обычно предсказывает близкую к фактической цене.

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

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

#_______________________________________REGULARIZATION_______________________________________
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
font = {'weight' : 'bold','size'   : 18}
plt.rc('font', **font)
generic_params = {'n_estimators' : 1000, 
                'loss' : 'huber',
                'min_samples_split' : 2, 
                'min_samples_leaf' : 2, 
                'random_state':random_state}

settings = [ ("learning_rate=1 subsample=1", "orange", {"learning_rate": 1.0, "subsample": 1.0}),
            ("learning_rate=.5 subsample=1 ", "green", {"learning_rate": 0.5, "subsample": 1.0}),
            ("learning_rate=1 subsample=0.5", "blue", {"learning_rate": 1, "subsample": 0.5}),
            ("learning_rate=0.5, subsample=0.5","gray", {"learning_rate": 0.5, "subsample": 0.5}),
            ("learning_rate=0.5, max_features=2", "magenta", {"learning_rate": 0.5, "max_features": 2})]

fig = plt.figure(figsize=(16, 16))
for label, color, setting in settings:
    params = generic_params
    params.update(setting)

    clf = GradientBoostingRegressor(**params)
    clf.fit(X_train, y_train)

    # compute test set deviance
    test_deviance = np.zeros((generic_params["n_estimators"],), dtype=np.float64)

    for i, y_proba in enumerate(clf.staged_predict(X_test)):
        test_deviance[i] = mean_squared_error(y_test, y_proba)

    plt.plot( (np.arange(test_deviance.shape[0]) + 1),test_deviance,color=color,label=label)

plt.legend(loc="upper right")
plt.xlabel("Boosting Iterations")
plt.ylabel("Test Set Deviance")
plt.title('REGULARIZATION')
plt.show()

Модель с наименьшим тестовым отклонением имеет скорость обучения 0,05 и одну подвыборку. Кроме того, очевидно, что после 200 итераций модель, кажется, достигла горизонтальной асимптоты, что указывает на то, что это хорошее место для ранней остановки. Поэтому окончательная модель, лучшая, чем предыдущая, в отношении обобщения и минимизации переобучения модели, строится с регуляризованными параметрами.

#_______________________________________FINAL PREDICTION _______________________________________
optim_params_reg = {'learning_rate': 0.05, 
                'n_estimators' : 300, 
                'loss' : 'huber',
                'min_samples_split' : 2, 
                'min_samples_leaf' : 2, 
                'random_state':random_state}
GradientBoostR = GradientBoostingRegressor(**optim_params_reg)
GradientBoostR.fit(X_train, y_train)

cv_results = cross_validate(GradientBoostR, X_actual, y_actual, cv=3, return_train_score=True, return_estimator=True, scoring=('r2'))
print("TEST score   : {:.1%}".format(np.mean(cv_results['test_score'])) , " (+/-) {:.1%}".format(np.std(cv_results['test_score'])))
print("TRAIN score  : {:.1%}".format(np.mean(cv_results['train_score'])), " (+/-) {:.1%}".format(np.std(cv_results['train_score'])))

После отправки моих прогнозируемых результатов в тестовом наборе данных (к которому моя модель не подошла) на Kaggle я получил оценку 0,138, что указывает на то, что моя модель была точной на 86,2%. Оценка теста, измеренная во время оценки окончательной модели, составила 87,5% (+/-) 2,4%. Поскольку измеренный результат теста и фактический результат теста находятся в пределах диапазона друг друга, можно сделать вывод, что модель хорошо обобщает.

Наконец, пришло время посмотреть на работы других Kagglers и пожалеть о своих. Наивысшие баллы использовали XGBoost, который является обновлением GradientBoost и обычно превосходит большинство других алгоритмов регрессии, включая GradientBoost. Тем не менее, меня впечатлила степень, в которой методы GridSearch, регуляризации и перекрестной проверки улучшили исходную модель, предполагая жизнеспособность настройки модели.

Это было мое первое соревнование на Kaggle, и оно мне понравилось. Я многому научился, как самостоятельно исследуя, так и изучая то, что делают другие. Чтобы ознакомиться с моим полным проектом, посетите https://github.com/brp221/Housing-Price-Regression