Прогнозирование цен на жилье с помощью творческой разработки функций и передовых методов регрессии | Лучшие 3%

Как новичок в области Data Scientist и когда я получил степень магистра в области анализа данных, я был мотивирован участвовать во многих соревнованиях Kaggle, чтобы применить на практике все, что я узнал во время учебы.

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

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

Здесь я расскажу вам, как я поднялся с 2800-го места до топ-150. Вы можете найти подробную записную книжку в моем профиле GitHub: https://github.com/chouhbik/Kaggle-House-Prices

Вначале я начал с создания различных моделей и обучения их на необработанных данных, которые я загрузил с Kaggle. Random Forest показал хорошие результаты с результатом 0,12825, достаточным для того, чтобы поместить меня в нижнюю часть таблицы лидеров.

После этого я понял, что предварительные этапы EDA (исследовательский анализ данных) и разработки функций очень важны. Далее я покажу вам, как я это улучшил…

Шаг 1. Логарифмическое преобразование зависимой переменной

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

(np.log(df_train["SalePrice"])).hist(bins = 40)

Шаг 2. Перекодирование предикторов / пропусков / выбросов

Некоторые из предикторов, которые являются порядковыми (т. Е. Их значения имеют естественный порядок) изначально сохраняются как факторы. Я их разумно перекодировал (примерами таких предикторов являются ExterQual и BsmtCond). Очевидно, мне пришлось заполнить недостающие значения:

  • Отсутствие числовых предикторов: я заполнял нулями, когда это имело смысл (например, если в подвале нет ванн, BsmtFullBath должен быть равен нулю),
  • Отсутствие категориальных предикторов: я ввел медианы или другие типичные значения (среднее значение или режим). Последний,
  • Также стоило удалить некоторые наблюдения с выбросами в предикторах, например TotalQual и GrLivArea.

Шаг 3. Работа с нелинейностями

Поскольку я планировал использовать некоторые линейные методы (Lasso, Ridge, SVM), я заменил все предикторы с тяжелым хвостом их журналами, а для некоторых предикторов добавил их квадраты ( т.е. у нас есть предиктор X и мы добавляем предиктор X²). Замена предикторов с тяжелыми хвостами на их журналы мотивирована тем, что:

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

Шаг 4. Добавление новых предикторов

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

Например:

# feture engineering a new feature "TotalSF"
all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']
all_data['YrBltAndRemod']=all_data['YearBuilt']+all_data['YearRemodAdd']

all_data['Total_sqr_footage'] = (all_data['BsmtFinSF1'] + all_data['BsmtFinSF2'] +all_data['1stFlrSF'] + all_data['2ndFlrSF'])

all_data['Total_Bathrooms'] = (all_data['FullBath'] + (0.5 * all_data['HalfBath']) + all_data['BsmtFullBath'] + (0.5 * all_data['BsmtHalfBath']))

all_data['Total_porch_sf'] = (all_data['OpenPorchSF'] +                         all_data['3SsnPorch'] + all_data['EnclosedPorch'] + all_data['ScreenPorch'] +all_data['WoodDeckSF'])
all_data['haspool'] = all_data['PoolArea'].apply(lambda x: 1 if x > 0 else 0)
all_data['has2ndfloor'] = all_data['2ndFlrSF'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasgarage'] = all_data['GarageArea'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasbsmt'] = all_data['TotalBsmtSF'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasfireplace'] = all_data['Fireplaces'].apply(lambda x: 1 if x > 0 else 0)

Шаг 5. Составление моделей

На следующем этапе я использовал 10-кратную перекрестную проверку и суммирование: для каждого «прогона» перекрестной проверки я пытался уместить 5 моделей на 9 из 10. сворачивает (Lasso, Ridge, Elastic-net, GBM и XGBoost), делайте прогнозы в левом сгибе и используйте эти пять наборов прогнозов в качестве входных данных для другого Lasso модель для прогнозирования журнала продаж в этой левой складке (такая модель Лассо называется метамоделью). Всего у нас есть 6 * 10 = 60 моделей (10 комплектов по 6 моделей). Все эти модели я использовал для окончательных прогнозов: я взял набор тестовых данных, сделал прогнозы с использованием 5 подмоделей, а затем использовал выходные данные этих моделей в качестве входных данных в соответствующую метамодель, чтобы получить набор прогнозов для данного набора моделей. Я повторил этот процесс 10 раз, чтобы получить 10 наборов прогнозов, а затем усреднил их, используя арифметическое среднее, чтобы получить данные, используемые для отправки.

Шаг 6. Настройка параметров

Для наложения я использовал 6 разных моделей, и для каждой требовалась настройка (для каждого «прогона» перекрестной проверки я использовал 6 моделей с одинаковыми параметрами. Я потратил много время и отправления для точной настройки параметров (лучшее улучшение было за счет настройки min_samples_leaf и min_samples_split для GradientBoostingRegressor ). В конце концов я получил smt около 0.1167 и был уверен, что не смогу улучшить его настройкой.

Шаг 7. Еще с пропущенными значениями

Затем я попробовал разные стратегии заполнения пропущенных значений (режимы / средства / медианы и т. Д.). Лучшее, что сработало, было в какой-то степени неожиданным: оно было основано на пакете mice в R и описано здесь. Я использовал пакет Mice, чтобы заполнить NA для числовой переменной, используя Случайный лес.

Пакет Мыши очень удобен при заполнении НА. Результат не самый лучший, но он лучше, чем использование медианы каждой переменной, чтобы заполнить пробел. Более того, он приятный и простой в использовании.

Используя R / Mice / SVM и python / stacking и взяв среднее геометрическое из этих прогнозов, я получил 0,1131, что уже было отличным результатом. Было неожиданно, что такой простой и не ориентированный на бизнес подход к заполнению выбросов сработал. Обратите внимание, что Мыши могут заполнить пропуски в числовых предикторах чем-то отличным от нуля, когда они должны быть равны нулю. Полагаю, что такой подход мне помог, так как он сильно отличался от того, что я получил в python.

Шаг 8. Жестокая сила

Я уже был доволен 0.1131, но это было последнее, что я хотел попробовать. Регрессия часто плохо работает для крайних случаев, для малых или больших значений предикторов. Я взял поезд, запустил R / mice / svm, python / stacking, усреднил результаты с использованием среднего геометрического, получил окончательные прогнозы для журнала продаж и построил их против журналов реальных значений:

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

Наконец, я попробовал кое-что довольно жесткое: я беру прогнозируемые продажи (не логарифмические, а продажи как таковые), беру 3 процентили и вручную увеличиваю / уменьшаю прогнозы.

Я повторил это 8 раз с разными значениями процентилей, пока в итоге не получил 0,10985. Важно отметить, что такой метод грубой силы улучшает прогнозы для небольшого количества наблюдений, но кажется, что этого достаточно для улучшения результатов в таблице лидеров.

q1 = final_sub['SalePrice'].quantile(0.0025)
q2 = final_sub['SalePrice'].quantile(0.0045)
q3 = final_sub['SalePrice'].quantile(0.99)

final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x > q1 else x*0.79)
final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x > q2 else x*0.89)
final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x < q3 else x*1.0)

Резюме:

В заключение, я попробовал еще кое-что, но не помогло:

  • PCA
  • Добавляем все больше и больше предикторов
  • Добавление других моделей в стопку
  • Использование keras для тензорного потока и масштабирования с плотными нейронными сетями.

Я бы сказал, что производственная версия будет иметь метрику оценки около 0,12, поскольку можно построить простую модель, которая даст те же результаты. Интересные вещи, которые помогли: добавление квадратов некоторых предикторов, использование Mice & SVM в R поверх необработанных данных и работа с предикторами (очень низкий или очень низкий уровень). высокие).

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