Лабораторная работа №5

Регрессия с применением Scikit-Learn — Предсказание цен на недвижимость

Выполнил: Лесницкий Александр Маркович P3121


1. Описание задачи

Задача — построить модель машинного обучения для прогнозирования стоимости домов в округе Кинг (штат Вашингтон, США) на основе их характеристик. Это задача регрессии: на выходе модели — непрерывное числовое значение (цена дома).

Датасет: Kaggle — House Sales in King County, USA
Период данных: май 2014 — май 2015. Всего 21 613 наблюдений, 21 переменная.

Доля обучающей выборки — 70% (15 129 объектов), тестовой — 30% (6 484 объекта).

Признаки датасета:

Столбец Описание
Целевая.Цена Целевая переменная: цена продажи дома
Жилая площадь Общая жилая площадь (кв. фут)
Оценка риелтора Оценка состояния объекта риелтором
Широта Широта расположения объекта
Спальни Количество спален
Количество этажей Этажность дома
Год реновации Год последней реновации (0 = не проводилась)
... 20 признаков итого

2. Ход работы

2.1. Загрузка и анализ данных

Данные загружены из файлов Excel с помощью pd.read_excel().

training_data = pd.read_excel('predict_house_price_training_data.xlsx')
test_data = pd.read_excel('predict_house_price_test_data.xlsx')

Метод info() подтвердил отсутствие пропусков: все 15 129 строк заполнены. Типы данных — int64 и float64.

2.2. Предобработка данных

Целевая переменная отделена от признаков:

target_variable_name = 'Целевая.Цена'
training_values = training_data[target_variable_name]
training_points = training_data.drop([target_variable_name], axis=1)

test_values = test_data[target_variable_name]
test_points = test_data.drop(target_variable_name, axis=1)

2.3. Обучение базовых моделей

Обучены две модели из библиотеки scikit-learn:

Линейная регрессия:

linear_regression_model = linear_model.LinearRegression()
linear_regression_model.fit(training_points, training_values)

Случайный лес:

random_forest_model = ensemble.RandomForestRegressor()
random_forest_model.fit(training_points, training_values)

2.4. Оценка базовых моделей

Метрики качества для задачи регрессии: - MAE (Mean Absolute Error) — средняя абсолютная ошибка - RMSE (Root Mean Squared Error) — корень из средней квадратичной ошибки; сильнее штрафует за большие ошибки

from sklearn.metrics import mean_absolute_error, mean_squared_error

mae_lr  = mean_absolute_error(test_values, test_predictions_linear)
rmse_lr = mean_squared_error(test_values, test_predictions_linear) ** 0.5

mae_rf  = mean_absolute_error(test_values, test_predictions_forest)
rmse_rf = mean_squared_error(test_values, test_predictions_forest) ** 0.5

Результаты:

Модель MAE RMSE
Линейная регрессия ~130 000 ~200 000
Random Forest (по умолчанию) ~75 000 ~130 000

Случайный лес значительно точнее линейной регрессии. На scatter-графике точки модели случайного леса плотнее прилегают к диагонали «идеального предсказания».


3. Анализ важности признаков

Обученная модель Random Forest позволяет оценить вклад каждого признака через feature_importances_:

feature_importance = pd.DataFrame({
    'Название признака': training_points.keys(),
    'Важность признака': random_forest_model.feature_importances_
})
feature_importance.sort_values(by='Важность признака', ascending=False)

Топ-5 наиболее важных признаков: 1. Оценка риелтора (~31%) 2. Жилая площадь (~29%) 3. Широта (~17%) 4. Год постройки (~7%) 5. Количество ванных комнат (~3%)

Наименее значимые признаки: - Год реновации — содержит 14 490 нулей из 15 129 строк, фактически неинформативен - Количество этажей - Спальни

Широта попала в топ, потому что центр Сиэтла расположен на севере округа — чем выше широта, тем ближе к центру города и дороже недвижимость.


4. Самостоятельная работа

4.1. Удаление незначащих признаков

Из обучающей и тестовой выборок исключены три наименее важных признака: Год реновации, Спальни, Количество этажей.

drop_cols = ['Год реновации', 'Спальни', 'Количество этажей']

train_points = training_data.drop([target_variable_name] + drop_cols, axis=1)
train_values = training_data[target_variable_name]

test_points = test_data.drop([target_variable_name] + drop_cols, axis=1)
test_values  = test_data[target_variable_name]

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

4.2. Подбор параметров Random Forest

Исследованы ключевые гиперпараметры модели:

  • n_estimators — количество деревьев (больше = точнее, но медленнее)
  • max_depth — максимальная глубина дерева
  • min_samples_leaf — минимальное число объектов в листе (регуляризация)
  • max_features — доля признаков при построении каждого дерева
model_forest = ensemble.RandomForestRegressor(
    n_estimators=1000,
    max_depth=None,
    min_samples_leaf=1,
    max_features=0.7,
    random_state=42,
    n_jobs=-1
)
model_forest.fit(train_points, train_values)
pred_forest = model_forest.predict(test_points)

RMSE = mean_squared_error(test_values, pred_forest) ** 0.5
MAE  = mean_absolute_error(test_values, pred_forest)
print(f"RandomForest (tuned) — MAE: {MAE:.2f}, RMSE: {RMSE:.2f}")

Увеличение n_estimators до 1000 и подбор max_features=0.7 снизили RMSE примерно на 8–12% относительно модели с параметрами по умолчанию.

4.3. Исследование дополнительных моделей

Gradient Boosting Regressor (sklearn)

from sklearn.ensemble import GradientBoostingRegressor

model_gb = GradientBoostingRegressor(
    n_estimators=1000,
    max_depth=6,
    learning_rate=0.01,
    max_features=0.3,
    min_samples_leaf=5,
    random_state=42
)
model_gb.fit(train_points, train_values)
pred_gb = model_gb.predict(test_points)

MLP Neural Network

Нейронная сеть требует предварительного масштабирования признаков:

from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
train_scaled = scaler.fit_transform(train_points)
test_scaled  = scaler.transform(test_points)

model_nn = MLPRegressor(hidden_layer_sizes=(100, 50), max_iter=500, random_state=42)
model_nn.fit(train_scaled, train_values)
pred_nn = model_nn.predict(test_scaled)

ElasticNet

Регуляризованная линейная регрессия, объединяющая Lasso и Ridge:

from sklearn.linear_model import ElasticNet

model_elastic = ElasticNet(alpha=1.0, l1_ratio=0.5)
model_elastic.fit(train_points, train_values)
pred_elastic = model_elastic.predict(test_points)

XGBoost

from xgboost import XGBRegressor

model_xgb = XGBRegressor(
    n_estimators=1000,
    max_depth=6,
    learning_rate=0.05,
    subsample=0.8,
    colsample_bytree=0.7,
    random_state=42
)
model_xgb.fit(train_points, train_values)
pred_xgb = model_xgb.predict(test_points)

4.4. Итоговое сравнение всех моделей

Модель MAE RMSE Примечание
Линейная регрессия ~130 000 ~200 000 Базовый ориентир
Random Forest (по умолчанию) ~75 000 ~130 000 Стандартные параметры
Random Forest (tuned) ~68 000 ~118 000 n_estimators=1000, max_features=0.7
Gradient Boosting ~70 000 ~120 000 learning_rate=0.01
MLP Neural Network ~90 000 ~145 000 Хуже на небольших данных
ElasticNet ~125 000 ~195 000 Слабее Random Forest
XGBoost ~65 000 ~112 000 Лучший результат

Вывод: Лучшую точность показал XGBoost. Настроенный Random Forest также превзошёл базовую версию. Нейронная сеть уступает ансамблевым методам на данном объёме данных. ElasticNet показал результат, близкий к обычной линейной регрессии.

Ключевые факторы улучшения качества: 1. Удаление неинформативных признаков (Год реновации) 2. Увеличение числа деревьев (n_estimators=1000) 3. Применение градиентного бустинга вместо простого усреднения


5. Интеграция модели с веб-сервисом (FastAPI)

Пошаговый алгоритм развёртывания обученной регрессионной модели в виде REST API:

Шаг 1. Сохранение модели и препроцессора

После обучения сериализуем объекты с помощью joblib:

import joblib
joblib.dump(model_xgb, 'house_price_model.pkl')
# если нужна нормализация:
joblib.dump(scaler, 'scaler.pkl')

Шаг 2. Описание схемы входных данных (Pydantic)

from pydantic import BaseModel

class HouseFeatures(BaseModel):
    bedrooms: int
    bathrooms: float
    sqft_living: int
    sqft_lot: int
    floors: float
    waterfront: int
    view: int
    condition: int
    grade: int
    sqft_above: int
    sqft_basement: int
    yr_built: int
    zipcode: int
    lat: float
    long: float

Шаг 3. Создание FastAPI-приложения

from fastapi import FastAPI
import joblib, numpy as np

app = FastAPI()
model = joblib.load('house_price_model.pkl')

@app.post("/predict")
def predict_price(data: HouseFeatures):
    features = np.array([[
        data.bedrooms, data.bathrooms, data.sqft_living,
        data.sqft_lot, data.floors, data.waterfront,
        data.view, data.condition, data.grade,
        data.sqft_above, data.sqft_basement, data.yr_built,
        data.zipcode, data.lat, data.long
    ]])
    predicted_price = model.predict(features)[0]
    return {
        "predicted_price_usd": round(float(predicted_price), 2)
    }

Шаг 4. Запуск сервера

pip install fastapi uvicorn joblib xgboost
uvicorn app:app --host 0.0.0.0 --port 8000 --reload

Шаг 5. Проверка через Swagger UI

FastAPI автоматически генерирует интерактивную документацию по адресу http://localhost:8000/docs — можно тестировать запросы прямо в браузере.

Шаг 6. Контейнеризация (Docker)

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Шаг 7. Деплой в облако

Готовый Docker-образ загружается в облачный реестр (GCP Artifact Registry, Yandex Container Registry) и запускается в виде сервиса (Cloud Run, Yandex Serverless Containers). При необходимости настраивается периодическое переобучение по актуальным данным.


6. Выводы

  • Задача предсказания цены на недвижимость является задачей регрессии; основные метрики — MAE и RMSE.
  • Random Forest значительно точнее линейной регрессии за счёт нелинейного моделирования зависимостей.
  • Анализ важности признаков выявил, что ключевыми предикторами являются оценка риелтора, жилая площадь и географическое положение (широта).
  • Удаление неинформативных признаков (Год реновации) дало небольшое улучшение качества.
  • Настройка гиперпараметров Random Forest (n_estimators=1000, max_features=0.7) снизила RMSE на ~10%.
  • XGBoost показал наилучший результат среди всех исследованных моделей.
  • Обученная модель легко оборачивается в REST API с помощью FastAPI и может быть развёрнута в облаке.

Ссылки