Сквозной проект — цены на жильё в Мельбурне
Практический маршрут для первой регрессионной модели на табличных данных: датасет Melbourne Housing (объявления о продаже жилья в Мельбурне, ~35 тыс. строк, 21 столбец), очистка в pandas, градиентный бустинг из scikit-learn, метрика MAE и настройка через GridSearchCV. Код можно запустить локально или в Kaggle Notebook — Jupyter не обязателен.
Датасет
| Источник | Melbourne Housing Market на Kaggle (зеркала встречаются под именем Melbourne_housing_FULL.csv) |
| Целевая переменная | Price — цена в австралийских долларах |
| Признаки (после базовой очистки) | Suburb, Rooms, Type, Distance, Bedroom2, Bathroom, Car, Landsize, BuildingArea, YearBuilt, CouncilArea |
В исходном файле опечатки в названиях столбцов (Lattitude, Longtitude) — их можно исправить в CSV или просто не использовать эти поля.
Шесть шагов pipeline
1–2. Импорт и загрузка
import pandas as pd
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn import ensemble
from sklearn.metrics import mean_absolute_error
df = pd.read_csv("Melbourne_housing_FULL.csv")
print(df.shape)
print(df.head())
Путь к файлу подставьте свой. Удобно держать CSV рядом с notebook или скриптом.
3. Очистка
Удаление лишних столбцов
Столбцы с адресом, датой продажи, координатами и служебными полями для базовой модели не нужны — пригород (Suburb) и район (CouncilArea) уже несут географию.
cols_to_drop = [
"Address", "Method", "SellerG", "Date", "Postcode",
"Lattitude", "Longtitude", "Regionname", "Propertycount",
]
df = df.drop(columns=[c for c in cols_to_drop if c in df.columns])
Удаление нерелевантных столбцов ускоряет обучение и снижает шум. Позже можно вернуть, например, SellerG (агентство) и проверить, улучшилась ли MAE на validation.
Пропуски
Для первого прохода — простой вариант: удалить строки с любыми NaN.
df = df.dropna(axis=0, how="any")
На проде чаще imputation (медиана, SimpleImputer) или отдельная обработка по столбцам — см. Кодирование категориальных признаков и Data Science.
Кодирование категорий
Категориальные Suburb, CouncilArea, Type переводим в числа one-hot через pd.get_dummies (подробнее — Кодирование категориальных признаков):
df = pd.get_dummies(df, columns=["Suburb", "CouncilArea", "Type"])
Подробнее о выборе кодировщика — Кодирование категориальных признаков.
X и y
X = df.drop("Price", axis=1)
y = df["Price"]
4. Разбиение 70 / 30
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, shuffle=True
)
random_state фиксирует воспроизводимость. Для финальной оценки не подбирайте гиперпараметры по test — только по train (или отдельному validation). Test — для финальной проверки; для перебора параметров нужна validation-выборка или k-fold на train.
5. Модель — градиентный бустинг
Стартовая конфигурация (агрессивная глубина → риск переобучения):
model = ensemble.GradientBoostingRegressor(
n_estimators=150,
learning_rate=0.1,
max_depth=30,
min_samples_split=4,
min_samples_leaf=6,
max_features=0.6,
loss="huber",
random_state=42,
)
model.fit(X_train, y_train)
| Гиперпараметр | Роль |
|---|---|
n_estimators | Число деревьев в ансамбле |
learning_rate | Вклад каждого следующего дерева |
max_depth | Глубина деревьев — главный рычаг bias/variance |
max_features | Доля признаков на split (как в random forest) |
loss='huber' | Устойчивость к выбросам в ценах |
6. Оценка — MAE
Mean Absolute Error — средняя абсолютная ошибка в тех же единицах, что и Price (AUD):
mae_train = mean_absolute_error(y_train, model.predict(X_train))
mae_test = mean_absolute_error(y_test, model.predict(X_test))
print(f"MAE train: {mae_train:,.0f}")
print(f"MAE test: {mae_test:,.0f}")
При max_depth=30 MAE на train получается намного ниже, чем на test — типичный сигнал переобучения: модель запомнила train, на новых домах ошибается сильнее.
Контекст: при ценах до миллионов AUD ошибка ~28 тыс. на train может быть приемлемой; разрыв train/test важнее абсолютного числа.
Оптимизация гиперпараметров
Смягчение переобучения вручную:
model_tuned = ensemble.GradientBoostingRegressor(
n_estimators=250,
learning_rate=0.1,
max_depth=5,
min_samples_split=4,
min_samples_leaf=6,
max_features=0.6,
loss="huber",
random_state=42,
)
model_tuned.fit(X_train, y_train)
max_depth с 30 до 5 — train MAE растёт, test часто улучшается, разрыв сужается. Меняйте по одному гиперпараметру и смотрите на обе выборки.
GridSearchCV
Автоматический перебор (может занять десятки минут на полном датасете):
Код ITЗагрузка примера кода…
cv=3 — кросс-валидация внутри train; test трогаем один раз в конце. Альтернатива при большой сетке — RandomizedSearchCV.
Не подбирайте столбцы, imputation и гиперпараметры, "подглядывая" в test. Весь GridSearchCV — только на train (или train+val). Иначе MAE на test будет оптимистичной.
Production-вариант — Pipeline
Чтобы кодирование и модель всегда применялись одинаково:
Код ITЗагрузка примера кода…
Такой каркас ближе к тому, что описано в Машинное обучение — предобработка.
Что дальше
- Сравнить с RandomForestRegressor или LinearRegression — baseline за 5 минут.
- Добавить validation-слой и early stopping для бустинга.
- Перейти к классификации — Lab 1157 — Titanic и Titanic на Kaggle.
- Углубить метрики регрессии (RMSE, R²) — Машинное обучение.