Перейти к основному содержанию
Ctrl+K
scikit-learn homepage scikit-learn homepage
  • Установить
  • Руководство пользователя
  • API
  • Примеры
  • Сообщество
    • Начало работы
    • История выпусков
    • Глоссарий
    • Разработка
    • Часто задаваемые вопросы
    • Поддержка
    • Связанные проекты
    • Дорожная карта
    • Управление
    • О нас
  • GitHub
  • Установить
  • Руководство пользователя
  • API
  • Примеры
  • Сообщество
  • Начало работы
  • История выпусков
  • Глоссарий
  • Разработка
  • Часто задаваемые вопросы
  • Поддержка
  • Связанные проекты
  • Дорожная карта
  • Управление
  • О нас
  • GitHub

Навигация по разделам

  • Основные изменения в выпуске
    • Основные нововведения в scikit-learn 1.8
    • Основные новости выпуска scikit-learn 1.7
    • Основные новости выпуска scikit-learn 1.6
    • Основные новости выпуска scikit-learn 1.5
    • Основные нововведения выпуска scikit-learn 1.4
    • Основные изменения в выпуске scikit-learn 1.3
    • Основные новости выпуска scikit-learn 1.2
    • Основные изменения в выпуске scikit-learn 1.1
    • Основные новости выпуска scikit-learn 1.0
    • Основные нововведения scikit-learn 0.24
    • Основные новости выпуска scikit-learn 0.23
    • Основные нововведения в выпуске scikit-learn 0.22
  • Бикластеризация
    • Демонстрация алгоритма Spectral Biclustering
    • Демонстрация алгоритма спектральной совместной кластеризации
    • Бикластеризация документов с помощью алгоритма спектральной совместной кластеризации
  • Калибровка
    • Сравнение калибровки классификаторов
    • Кривые калибровки вероятности
    • Калибровка вероятностей для классификации на 3 класса
    • Калибровка вероятностей классификаторов
  • Классификация
    • Сравнение классификаторов
    • Линейный и квадратичный дискриминантный анализ с эллипсоидом ковариации
    • Нормальный, Ledoit-Wolf и OAS линейный дискриминантный анализ для классификации
    • Построить график вероятности классификации
    • Распознавание рукописных цифр
  • Кластеризация
    • Демонстрация кластеризации K-Means на данных рукописных цифр
    • Демонстрация структурированной иерархической кластеризации Уорда на изображении монет
    • Демонстрация алгоритма кластеризации mean-shift
    • Коррекция на случайность в оценке производительности кластеризации
    • Агломеративная кластеризация с разными метриками
    • Пример инициализации K-Means++
    • Сравнение производительности биссекционного K-средних и обычного K-средних
    • Сравнение BIRCH и MiniBatchKMeans
    • Сравнение различных алгоритмов кластеризации на игрушечных наборах данных
    • Сравнение различных методов иерархической связи на игрушечных наборах данных
    • Сравнение алгоритмов кластеризации K-Means и MiniBatchKMeans
    • Демонстрация алгоритма кластеризации DBSCAN
    • Демонстрация алгоритма кластеризации HDBSCAN
    • Демонстрация алгоритма кластеризации OPTICS
    • Демонстрация алгоритма кластеризации с распространением аффинности
    • Демонстрация предположений k-means
    • Эмпирическая оценка влияния инициализации k-means
    • Агломерация признаков
    • Агломерация признаков против одномерного отбора
    • Иерархическая кластеризация со структурой и без
    • Индуктивная кластеризация
    • Онлайн-обучение словаря частей лиц
    • Построить дендрограмму иерархической кластеризации
    • Сегментация изображения греческих монет на регионы
    • Выбор количества кластеров с помощью анализа силуэта для кластеризации KMeans
    • Спектральная кластеризация для сегментации изображений
    • Различные агломеративные кластеризации на 2D-вложении цифр
    • Пример векторного квантования
  • Оценка ковариации
    • Оценка Ледойта-Вольфа против OAS оценки
    • Робастная оценка ковариации и релевантность расстояний Махаланобиса
    • Робастная vs эмпирическая оценка ковариации
    • Оценка ковариации сжатием: LedoitWolf vs OAS и максимальное правдоподобие
    • Оценка разреженной обратной ковариации
  • Перекрестное разложение
    • Сравнить методы перекрёстного разложения
    • Регрессия на главных компонентах против регрессии методом частичных наименьших квадратов
  • Примеры наборов данных
    • Построение случайно сгенерированного многометочного набора данных
  • Деревья решений
    • Регрессия дерева решений
    • Построить поверхность решений деревьев решений, обученных на наборе данных ирисов
    • Пост-обрезка деревьев решений с обрезкой по стоимости сложности
    • Понимание структуры дерева решений
  • Разложение
    • Разделение слепых источников с использованием FastICA
    • Сравнение LDA и PCA 2D проекции набора данных Iris
    • Разложения набора данных Faces
    • Факторный анализ (с вращением) для визуализации паттернов
    • FastICA на 2D облаках точек
    • Удаление шума изображений с использованием обучения словаря
    • Инкрементальный PCA
    • Ядерный PCA
    • Выбор модели с вероятностным PCA и факторным анализом (FA)
    • Анализ главных компонент (PCA) на наборе данных Iris
    • Разреженное кодирование с предвычисленным словарём
  • Разработка оценщиков
    • __sklearn_is_fitted__ как API для разработчиков
  • Ансамблевые методы
    • Поддержка категориальных признаков в градиентном бустинге
    • Объедините предикторы с помощью стекинга
    • Сравнение моделей случайных лесов и градиентного бустинга на гистограммах
    • Сравнение случайных лесов и мета-оценщика с множественным выходом
    • Регрессия решающего дерева с AdaBoost
    • Ранняя остановка в градиентном бустинге
    • Важность признаков с использованием леса деревьев
    • Преобразования признаков с ансамблями деревьев
    • Признаки в деревьях с градиентным бустингом на гистограммах
    • Оценки Gradient Boosting Out-of-Bag
    • Градиентный бустинг для регрессии
    • Регуляризация градиентного бустинга
    • Преобразование признаков с хешированием с использованием полностью случайных деревьев
    • Пример IsolationForest
    • Монотонные ограничения
    • Многоклассовые деревья решений с бустингом AdaBoost
    • Ошибки OOB для случайных лесов
    • Построить индивидуальные и голосующие регрессионные предсказания
    • Построить поверхности решений ансамблей деревьев на наборе данных ирисов
    • Интервалы прогнозирования для регрессии градиентного бустинга
    • Один оценщик против бэггинга: декомпозиция смещения-дисперсии
    • Двухклассовый AdaBoost
    • Визуализация вероятностных предсказаний VotingClassifier
  • Примеры на основе реальных наборов данных
    • Компрессионное зондирование: реконструкция томографии с априорным распределением L1 (Lasso)
    • Пример распознавания лиц с использованием собственных лиц и SVM
    • Удаление шума с изображения с использованием ядерного PCA
    • Лаггированные признаки для прогнозирования временных рядов
    • Влияние сложности модели
    • Классификация текстовых документов вне памяти
    • Обнаружение выбросов на реальном наборе данных
    • Задержка предсказания
    • Моделирование распределения видов
    • Инженерия временных признаков
    • Извлечение тем с использованием неотрицательной матричной факторизации и латентного размещения Дирихле
    • Визуализация структуры фондового рынка
    • Главный собственный вектор Википедии
  • Выбор признаков
    • Сравнение F-теста и взаимной информации
    • Основанный на модели и последовательный отбор признаков
    • Конвейер ANOVA SVM
    • Рекурсивное исключение признаков
    • Рекурсивное исключение признаков с перекрестной проверкой
    • Одномерный отбор признаков
  • Замороженные оценщики
    • Примеры использования FrozenEstimator
  • Гауссовские смеси моделей
    • Анализ вариации байесовской гауссовой смеси с априорным типом концентрации
    • Оценка плотности для гауссовской смеси
    • Методы инициализации GMM
    • Ковариации GMM
    • Эллипсоиды гауссовской смеси
    • Выбор модели гауссовской смеси
    • Гауссова смесь моделей синусоидальной кривой
  • Гауссовский процесс для машинного обучения
    • Способность гауссовского процесса регрессии (GPR) оценивать уровень шума данных
    • Сравнение ядерной гребневой регрессии и регрессии по методу Гауссовских процессов
    • Прогнозирование уровня CO2 на наборе данных Mona Loa с использованием гауссовской регрессии (GPR)
    • Регрессия гауссовских процессов: базовый вводный пример
    • Гауссовский процесс классификации (GPC) на наборе данных iris
    • Гауссовские процессы на дискретных структурах данных
    • Иллюстрация классификации гауссовским процессом (GPC) на наборе данных XOR
    • Иллюстрация априорного и апостериорного гауссовских процессов для различных ядер
    • Изолинии равной вероятности для классификации гауссовских процессов (GPC)
    • Вероятностные предсказания с гауссовским процессом классификации (GPC)
  • Обобщенные линейные модели
    • Сравнение линейных байесовских регрессоров
    • Аппроксимация кривой с использованием байесовской гребневой регрессии
    • Границы решений мультиномиальной и логистической регрессии One-vs-Rest
    • Ранняя остановка стохастического градиентного спуска
    • Обучение Elastic Net с предвычисленной матрицей Грама и взвешенными выборками
    • HuberRegressor против Ridge на наборе данных с сильными выбросами
    • Совместный отбор признаков с многозадачным Lasso
    • L1-штраф и разреженность в логистической регрессии
    • Модели на основе L1 для разреженных сигналов
    • Выбор модели Lasso с помощью информационных критериев
    • Выбор модели Lasso: AIC-BIC / перекрёстная проверка
    • Lasso на плотных и разреженных данных
    • Пути Lasso, Lasso-LARS и Elastic Net
    • Классификация MNIST с использованием мультиномиальной логистической регрессии + L1
    • Многоклассовая разреженная логистическая регрессия на 20newsgroups
    • Метод наименьших квадратов с неотрицательными ограничениями
    • One-Class SVM против One-Class SVM с использованием стохастического градиентного спуска
    • Метод наименьших квадратов и гребневая регрессия
    • Orthogonal Matching Pursuit
    • Построение коэффициентов Ridge как функции регуляризации
    • Построение многоклассового SGD на наборе данных iris
    • Пуассоновская регрессия и ненормальные потери
    • Полиномиальная и сплайновая интерполяция
    • Квантильная регрессия
    • Регуляризационный путь L1-логистической регрессии
    • Коэффициенты Ridge как функция L2-регуляризации
    • Робастная оценка линейной модели
    • Робастная оценка линейной модели с использованием RANSAC
    • SGD: Гиперплоскость максимального разделяющего запаса
    • SGD: Штрафы
    • SGD: Взвешенные выборки
    • SGD: выпуклые функции потерь
    • Регрессия Тейла-Сена
    • Регрессия Твиди для страховых случаев
  • Инспекция
    • Распространённые ошибки в интерпретации коэффициентов линейных моделей
    • Неспособность машинного обучения выводить причинно-следственные связи
    • Графики частичной зависимости и индивидуального условного ожидания
    • Важность перестановок против важности признаков случайного леса (MDI)
    • Важность перестановок с мультиколлинеарными или коррелированными признаками
  • Аппроксимация ядра
    • Масштабируемое обучение с полиномиальной аппроксимацией ядра
  • Обучение многообразию
    • Сравнение методов обучения многообразий
    • Методы обучения многообразий на разрезанной сфере
    • Обучение многообразию на рукописных цифрах: Locally Linear Embedding, Isomap…
    • load_files
    • Снижение размерности Swiss Roll и Swiss-Hole
    • t-SNE: Влияние различных значений perplexity на форму
  • Разное
    • Расширенное построение графиков с частичной зависимостью
    • Сравнение алгоритмов обнаружения аномалий для выявления выбросов на игрушечных наборах данных
    • Сравнение ядерной регрессии гребня и SVR
    • Отображение конвейеров
    • Отображение оценщиков и сложных конвейеров
    • Оценка оценщиков обнаружения выбросов
    • Аппроксимация явного отображения признаков для RBF-ядер
    • Завершение лица с помощью многоканальных оценщиков
    • Представляем set_output API
    • Изотоническая регрессия
    • Маршрутизация метаданных
    • Многометочная классификация
    • Кривая ROC с API визуализации
    • Граница Джонсона-Линденштрауса для вложения с помощью случайных проекций
    • Визуализации с объектами Display
  • Импутация пропущенных значений
    • Заполнение пропущенных значений перед построением оценщика
    • Заполнение пропущенных значений с вариантами IterativeImputer
  • Выбор модели
    • Баланс сложности модели и кросс-валидационной оценки
    • Graham Clenaghan
    • Сравнение рандомизированного поиска и поиска по сетке для оценки гиперпараметров
    • Сравнение между поиском по сетке и последовательным сокращением вдвое
    • Пользовательская стратегия повторного обучения для поиска по сетке с кросс-валидацией
    • Демонстрация многометрической оценки на cross_val_score и GridSearchCV
    • Кривая компромисса ошибок обнаружения (DET)
    • Влияние регуляризации модели на ошибку обучения и тестирования
    • Оценить производительность классификатора с помощью матрицы ошибок
    • Многоклассовая рабочая характеристика приемника (ROC)
    • Вложенная и невложенная перекрестная проверка
    • Построение перекрестно проверенных предсказаний
    • Построение кривых обучения и проверка масштабируемости моделей
    • Пост-фактумная настройка точки отсечения функции принятия решений
    • Последующая настройка порога принятия решений для обучения с учетом стоимости
    • Precision-Recall
    • Рабочая характеристика приёмника (ROC) с перекрёстной проверкой
    • Примерный пайплайн для извлечения и оценки текстовых признаков
    • Статистическое сравнение моделей с использованием поиска по сетке
    • Последовательные итерации деления пополам
    • Тест с перестановками для значимости оценки классификации
    • Недообучение vs. Переобучение
    • Визуализация поведения кросс-валидации в scikit-learn
  • Многоклассовые методы
    • Обзор мета-оценщиков для многоклассового обучения
  • Многовариантные методы
    • Многометочная классификация с использованием цепочки классификаторов
  • Ближайшие соседи
    • Приближенные ближайшие соседи в TSNE
    • Кэширование ближайших соседей
    • Сравнение ближайших соседей с анализом компонент соседства и без него
    • Снижение размерности с помощью анализа компонентов соседства
    • Оценка плотности ядра распределения видов
    • Оценка плотности ядра
    • Классификация по ближайшему центроиду
    • Классификация методом ближайших соседей
    • Регрессия методом ближайших соседей
    • Иллюстрация анализа компонентов соседства
    • Обнаружение новизны с помощью локального фактора выбросов (LOF)
    • Обнаружение выбросов с помощью фактора локальных выбросов (LOF)
    • Простое одномерное ядерное сглаживание плотности
  • Нейронные сети
    • Сравнение стохастических стратегий обучения для MLPClassifier
    • Признаки ограниченной машины Больцмана для классификации цифр
    • Изменение регуляризации в многослойном перцептроне
    • Visualization of MLP weights on MNIST
  • Конвейеры и составные оценщики
    • Трансформер столбцов с разнородными источниками данных
    • Трансформер столбцов со смешанными типами
    • Объединение нескольких методов извлечения признаков
    • Эффект преобразования целей в регрессионной модели
    • Конвейеризация: объединение PCA и логистической регрессии
    • Выбор уменьшения размерности с помощью Pipeline и GridSearchCV
  • Предобработка
    • Сравнение влияния различных масштабировщиков на данные с выбросами
    • Сравнение Target Encoder с другими кодировщиками
    • Демонстрация различных стратегий KBinsDiscretizer
    • Дискретизация признаков
    • Важность масштабирования признаков
    • Преобразует данные в нормальное распределение
    • Внутренняя перекрестная подгонка Target Encoder
    • Использование KBinsDiscretizer для дискретизации непрерывных признаков
  • Полуавтоматическая классификация
    • Граница решения полуконтролируемых классификаторов против SVM на наборе данных Iris
    • Влияние изменения порога для самообучения
    • Распространение меток по кругам: Обучение сложной структуре
    • Распространение меток цифр: Активное обучение
    • Распространение меток на цифрах: Демонстрация производительности
    • Полу-контролируемая классификация на текстовом наборе данных
  • Метод опорных векторов
    • Одноклассовый SVM с нелинейным ядром (RBF)
    • Построение границ классификации с различными ядрами SVM
    • Построение различных классификаторов SVM на наборе данных iris
    • Построить опорные векторы в LinearSVC
    • Параметры SVM с RBF-ядром
    • Пример границ SVM
    • Пример разрешения ничьей в SVM
    • SVM с пользовательским ядром
    • SVM-Anova: SVM с одномерным отбором признаков
    • SVM: Разделяющая гиперплоскость с максимальным зазором
    • SVM: Разделяющая гиперплоскость для несбалансированных классов
    • SVM: Взвешенные образцы
    • Масштабирование параметра регуляризации для SVC
    • Регрессия на основе метода опорных векторов (SVR) с использованием линейных и нелинейных ядер
  • Работа с текстовыми документами
    • Классификация текстовых документов с использованием разреженных признаков
    • Кластеризация текстовых документов с использованием k-means
    • Сравнение FeatureHasher и DictVectorizer
  • Примеры
  • Примеры на основе реальных наборов данных
  • Инженерия временных признаков

Примечание

Перейти в конец чтобы скачать полный пример кода или запустить этот пример в браузере через JupyterLite или Binder.

Инженерия временных признаков#

Эта записная книжка представляет различные стратегии использования временных признаков для задачи регрессии спроса на прокат велосипедов, которая сильно зависит от бизнес-циклов (дни, недели, месяцы) и годовых сезонных циклов.

В процессе мы представляем, как выполнять периодическое конструирование признаков с использованием sklearn.preprocessing.SplineTransformer класс и его extrapolation="periodic" опция.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Исследование данных на наборе Bike Sharing Demand#

Начнем с загрузки данных из репозитория OpenML.

from sklearn.datasets import fetch_openml

bike_sharing = fetch_openml("Bike_Sharing_Demand", version=2, as_frame=True)
df = bike_sharing.frame

Чтобы быстро понять периодические закономерности данных, давайте посмотрим на средний спрос в час в течение недели.

Обратите внимание, что неделя начинается в воскресенье, в выходные дни. Мы можем четко различить модели поездок утром и вечером в рабочие дни и использование велосипедов для отдыха в выходные с более широким пиком спроса в середине дня:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(12, 4))
average_week_demand = df.groupby(["weekday", "hour"])["count"].mean()
average_week_demand.plot(ax=ax)
_ = ax.set(
    title="Average hourly bike demand during the week",
    xticks=[i * 24 for i in range(7)],
    xticklabels=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
    xlabel="Time of the week",
    ylabel="Number of bike rentals",
)
Average hourly bike demand during the week

Целью задачи прогнозирования является абсолютное количество аренд велосипедов в почасовом разрезе:

df["count"].max()
np.int64(977)

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

Примечание

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

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

y = df["count"] / df["count"].max()
fig, ax = plt.subplots(figsize=(12, 4))
y.hist(bins=30, ax=ax)
_ = ax.set(
    xlabel="Fraction of rented fleet demand",
    ylabel="Number of hours",
)
plot cyclical feature engineering

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

X = df.drop("count", axis="columns")
X
сезон год month hour праздник день недели workingday погода temp ощущаемая температура влажность скорость ветра
0 spring 0 1 0 False 6 False clear 9.84 14.395 0.81 0.0000
1 spring 0 1 1 False 6 False clear 9.02 13.635 0.80 0.0000
2 spring 0 1 2 False 6 False clear 9.02 13.635 0.80 0.0000
3 spring 0 1 3 False 6 False clear 9.84 14.395 0.75 0.0000
4 spring 0 1 4 False 6 False clear 9.84 14.395 0.75 0.0000
... ... ... ... ... ... ... ... ... ... ... ... ...
17374 spring 1 12 19 False 1 True misty 10.66 12.880 0.60 11.0014
17375 spring 1 12 20 False 1 True misty 10.66 12.880 0.60 11.0014
17376 spring 1 12 21 False 1 True clear 10.66 12.880 0.60 11.0014
17377 spring 1 12 22 False 1 True clear 10.66 13.635 0.56 8.9981
17378 spring 1 12 23 False 1 True clear 10.66 13.635 0.65 8.9981

17379 строк × 12 столбцов



Примечание

Если временная информация присутствовала только в виде столбца с датой или датой-временем, мы могли бы разложить её на час дня, день недели, день месяца, месяц года с помощью pandas: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#time-date-components

Теперь мы исследуем распределение категориальных переменных, начиная с "weather":

X["weather"].value_counts()
weather
clear         11413
misty          4544
rain           1419
heavy_rain        3
Name: count, dtype: int64

Поскольку есть только 3 "heavy_rain" события, мы не можем использовать эту категорию для обучения моделей машинного обучения с перекрестной проверкой. Вместо этого мы упрощаем представление, объединяя их в "rain" категория.

X["weather"] = (
    X["weather"]
    .astype(object)
    .replace(to_replace="heavy_rain", value="rain")
    .astype("category")
)
X["weather"].value_counts()
weather
clear    11413
misty     4544
rain      1422
Name: count, dtype: int64

Как и ожидалось, "season" переменная хорошо сбалансирована:

X["season"].value_counts()
season
fall      4496
summer    4409
spring    4242
winter    4232
Name: count, dtype: int64

Кросс-валидация на основе времени#

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

1000 тестовых точек данных должно быть достаточно для оценки производительности модели. Это представляет собой чуть меньше полутора месяцев непрерывных тестовых данных:

from sklearn.model_selection import TimeSeriesSplit

ts_cv = TimeSeriesSplit(
    n_splits=5,
    gap=48,
    max_train_size=10000,
    test_size=1000,
)

Давайте вручную проверим различные разделения, чтобы убедиться, что TimeSeriesSplit работает, как мы ожидаем, начиная с первого разделения:

all_splits = list(ts_cv.split(X, y))
train_0, test_0 = all_splits[0]
X.iloc[test_0]
сезон год month hour праздник день недели workingday погода temp ощущаемая температура влажность скорость ветра
12379 лето 1 6 0 False 2 True clear 22.14 25.760 0.68 27.9993
12380 лето 1 6 1 False 2 True misty 21.32 25.000 0.77 22.0028
12381 лето 1 6 2 False 2 True дождь 21.32 25.000 0.72 19.9995
12382 лето 1 6 3 False 2 True дождь 20.50 24.240 0.82 12.9980
12383 лето 1 6 4 False 2 True дождь 20.50 24.240 0.82 12.9980
... ... ... ... ... ... ... ... ... ... ... ... ...
13374 осень 1 7 11 False 1 True clear 34.44 40.150 0.53 15.0013
13375 осень 1 7 12 False 1 True clear 34.44 39.395 0.49 8.9981
13376 осень 1 7 13 False 1 True clear 34.44 39.395 0.49 19.0012
13377 осень 1 7 14 False 1 True clear 36.08 40.910 0.42 7.0015
13378 осень 1 7 15 False 1 True clear 35.26 40.150 0.47 16.9979

1000 строк × 12 столбцов



X.iloc[train_0]
сезон год month hour праздник день недели workingday погода temp ощущаемая температура влажность скорость ветра
2331 лето 0 4 1 False 2 True misty 25.42 31.060 0.50 6.0032
2332 лето 0 4 2 False 2 True misty 24.60 31.060 0.53 8.9981
2333 лето 0 4 3 False 2 True misty 23.78 27.275 0.56 8.9981
2334 лето 0 4 4 False 2 True misty 22.96 26.515 0.64 8.9981
2335 лето 0 4 5 False 2 True misty 22.14 25.760 0.68 8.9981
... ... ... ... ... ... ... ... ... ... ... ... ...
12326 лето 1 6 19 False 6 False clear 26.24 31.060 0.36 11.0014
12327 лето 1 6 20 False 6 False clear 25.42 31.060 0.35 19.0012
12328 лето 1 6 21 False 6 False clear 24.60 31.060 0.40 7.0015
12329 лето 1 6 22 False 6 False clear 23.78 27.275 0.46 8.9981
12330 лето 1 6 23 False 6 False clear 22.96 26.515 0.52 7.0015

10000 строк × 12 столбцов



Теперь мы исследуем последнее разделение:

train_4, test_4 = all_splits[4]
X.iloc[test_4]
сезон год month hour праздник день недели workingday погода temp ощущаемая температура влажность скорость ветра
16379 зима 1 11 5 False 2 True misty 13.94 16.665 0.66 8.9981
16380 зима 1 11 6 False 2 True misty 13.94 16.665 0.71 11.0014
16381 зима 1 11 7 False 2 True clear 13.12 16.665 0.76 6.0032
16382 зима 1 11 8 False 2 True clear 13.94 16.665 0.71 8.9981
16383 зима 1 11 9 False 2 True misty 14.76 18.940 0.71 0.0000
... ... ... ... ... ... ... ... ... ... ... ... ...
17374 spring 1 12 19 False 1 True misty 10.66 12.880 0.60 11.0014
17375 spring 1 12 20 False 1 True misty 10.66 12.880 0.60 11.0014
17376 spring 1 12 21 False 1 True clear 10.66 12.880 0.60 11.0014
17377 spring 1 12 22 False 1 True clear 10.66 13.635 0.56 8.9981
17378 spring 1 12 23 False 1 True clear 10.66 13.635 0.65 8.9981

1000 строк × 12 столбцов



X.iloc[train_4]
сезон год month hour праздник день недели workingday погода temp ощущаемая температура влажность скорость ветра
6331 зима 0 9 9 False 1 True misty 26.24 28.790 0.89 12.9980
Ке и др. зима 0 9 10 False 1 True misty 26.24 28.790 0.89 12.9980
6333 зима 0 9 11 False 1 True clear 27.88 31.820 0.79 15.0013
6334 зима 0 9 12 False 1 True misty 27.88 31.820 0.79 11.0014
6335 зима 0 9 13 False 1 True misty 28.70 33.335 0.74 11.0014
... ... ... ... ... ... ... ... ... ... ... ... ...
16326 зима 1 11 0 False 0 False misty 12.30 15.150 0.70 11.0014
16327 зима 1 11 1 False 0 False clear 12.30 14.395 0.70 12.9980
16328 зима 1 11 2 False 0 False clear 11.48 14.395 0.81 7.0015
16329 зима 1 11 3 False 0 False misty 12.30 15.150 0.81 11.0014
16330 зима 1 11 4 False 0 False misty 12.30 14.395 0.81 12.9980

10000 строк × 12 столбцов



Всё хорошо. Теперь мы готовы заняться прогнозным моделированием!

Градиентный бустинг#

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

Здесь мы используем современный HistGradientBoostingRegressor с нативной поддержкой категориальных признаков. Поэтому нам нужно только установить categorical_features="from_dtype" таким образом, что признаки с категориальным типом данных считаются категориальными признаками. Для справки мы извлекаем категориальные признаки из датафрейма на основе типа данных. Внутренние деревья используют специальное правило разделения для этих признаков.

Числовые переменные не требуют предварительной обработки, и для простоты мы пробуем только гиперпараметры по умолчанию для этой модели:

from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.model_selection import cross_validate
from sklearn.pipeline import make_pipeline

gbrt = HistGradientBoostingRegressor(categorical_features="from_dtype", random_state=42)
categorical_columns = X.columns[X.dtypes == "category"]
print("Categorical features:", categorical_columns.tolist())
Categorical features: ['season', 'holiday', 'workingday', 'weather']

Давайте оценим нашу модель градиентного бустинга со средней абсолютной ошибкой относительного спроса, усредненной по нашим 5 временным кросс-валидационным разбиениям:

import numpy as np


def evaluate(model, X, y, cv, model_prop=None, model_step=None):
    cv_results = cross_validate(
        model,
        X,
        y,
        cv=cv,
        scoring=["neg_mean_absolute_error", "neg_root_mean_squared_error"],
        return_estimator=model_prop is not None,
    )
    if model_prop is not None:
        if model_step is not None:
            values = [
                getattr(m[model_step], model_prop) for m in cv_results["estimator"]
            ]
        else:
            values = [getattr(m, model_prop) for m in cv_results["estimator"]]
        print(f"Mean model.{model_prop} = {np.mean(values)}")
    mae = -cv_results["test_neg_mean_absolute_error"]
    rmse = -cv_results["test_neg_root_mean_squared_error"]
    print(
        f"Mean Absolute Error:     {mae.mean():.3f} +/- {mae.std():.3f}\n"
        f"Root Mean Squared Error: {rmse.mean():.3f} +/- {rmse.std():.3f}"
    )


evaluate(gbrt, X, y, cv=ts_cv, model_prop="n_iter_")
Mean model.n_iter_ = 100.0
Mean Absolute Error:     0.044 +/- 0.003
Root Mean Squared Error: 0.068 +/- 0.005

Мы видим, что мы установили max_iter достаточно большим, чтобы произошла ранняя остановка.

Эта модель имеет среднюю ошибку около 4–5% от максимального спроса. Это довольно хорошо для первого испытания без какой-либо настройки гиперпараметров! Нам просто нужно было сделать категориальные переменные явными. Обратите внимание, что временные признаки передаются как есть, т.е. без их обработки. Но это не является большой проблемой для моделей на основе деревьев, так как они могут изучить немонотонную связь между порядковыми входными признаками и целевой переменной.

Это не относится к моделям линейной регрессии, как мы увидим далее.

Наивная линейная регрессия#

Как обычно для линейных моделей, категориальные переменные должны быть закодированы методом one-hot. Для согласованности мы масштабируем числовые признаки до того же диапазона 0-1, используя MinMaxScaler, хотя в данном случае это не сильно влияет на результаты, поскольку они уже находятся в сопоставимых масштабах:

from sklearn.linear_model import RidgeCV
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder

one_hot_encoder = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
alphas = np.logspace(-6, 6, 25)
naive_linear_pipeline = make_pipeline(
    ColumnTransformer(
        transformers=[
            ("categorical", one_hot_encoder, categorical_columns),
        ],
        remainder=MinMaxScaler(),
    ),
    RidgeCV(alphas=alphas),
)


evaluate(
    naive_linear_pipeline, X, y, cv=ts_cv, model_prop="alpha_", model_step="ridgecv"
)
Mean model.alpha_ = 2.7298221281347037
Mean Absolute Error:     0.142 +/- 0.014
Root Mean Squared Error: 0.184 +/- 0.020

Утвердительно видеть, что выбранный alpha_ Значение наименьшего коэффициента между 0 и 1.

Производительность неудовлетворительна: средняя ошибка составляет около 14% от максимального спроса. Это более чем в три раза выше, чем средняя ошибка модели градиентного бустинга. Можно предположить, что наивное исходное кодирование (просто масштабированное по минимуму-максимуму) периодических временных признаков может препятствовать линейной регрессионной модели должным образом использовать временную информацию: линейная регрессия не моделирует автоматически немонотонные связи между входными признаками и целевой переменной. Нелинейные члены должны быть сконструированы во входных данных.

Например, исходное числовое кодирование "hour" особенность предотвращает распознавание линейной моделью того, что увеличение часа утром с 6 до 8 должно сильно положительно влиять на количество аренд велосипедов, в то время как увеличение аналогичной величины вечером с 18 до 20 должно сильно отрицательно влиять на прогнозируемое количество аренд велосипедов.

Временные шаги как категории#

Поскольку временные признаки закодированы дискретным образом с использованием целых чисел (24 уникальных значения в признаке "часы"), мы можем решить обрабатывать их как категориальные переменные с использованием one-hot кодирования и тем самым игнорировать любые предположения, подразумеваемые упорядочиванием значений часов.

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

one_hot_linear_pipeline = make_pipeline(
    ColumnTransformer(
        transformers=[
            ("categorical", one_hot_encoder, categorical_columns),
            ("one_hot_time", one_hot_encoder, ["hour", "weekday", "month"]),
        ],
        remainder=MinMaxScaler(),
    ),
    RidgeCV(alphas=alphas),
)

evaluate(one_hot_linear_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.099 +/- 0.011
Root Mean Squared Error: 0.131 +/- 0.011

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

Однако это вводит очень большое количество новых признаков. Если бы время дня было представлено в минутах с начала дня вместо часов, one-hot кодирование создало бы 1440 признаков вместо 24. Это может вызвать значительное переобучение. Чтобы избежать этого, мы можем использовать sklearn.preprocessing.KBinsDiscretizer вместо этого для перераспределения количества уровней мелкозернистых порядковых или числовых переменных, при этом все еще получая преимущества немонотонной выразительности one-hot кодирования.

Наконец, мы также наблюдаем, что one-hot encoding полностью игнорирует порядок уровней часа, хотя это может быть интересным индуктивным смещением, которое стоит сохранить до некоторой степени. Далее мы попытаемся исследовать гладкое, немонотонное кодирование, которое локально сохраняет относительный порядок временных признаков.

Тригонометрические признаки#

В качестве первой попытки мы можем попробовать закодировать каждый из этих периодических признаков с использованием преобразования синуса и косинуса с соответствующим периодом.

Каждый порядковый временной признак преобразуется в 2 признака, которые вместе кодируют эквивалентную информацию не монотонным способом, и, что более важно, без любого скачка между первым и последним значением периодического диапазона.

from sklearn.preprocessing import FunctionTransformer


def sin_transformer(period):
    return FunctionTransformer(lambda x: np.sin(x / period * 2 * np.pi))


def cos_transformer(period):
    return FunctionTransformer(lambda x: np.cos(x / period * 2 * np.pi))

Давайте визуализируем эффект этого расширения признаков на некоторых синтетических данных о часах с небольшой экстраполяцией за пределы hour=23:

import pandas as pd

hour_df = pd.DataFrame(
    np.arange(26).reshape(-1, 1),
    columns=["hour"],
)
hour_df["hour_sin"] = sin_transformer(24).fit_transform(hour_df)["hour"]
hour_df["hour_cos"] = cos_transformer(24).fit_transform(hour_df)["hour"]
hour_df.plot(x="hour")
_ = plt.title("Trigonometric encoding for the 'hour' feature")
Trigonometric encoding for the 'hour' feature

Давайте используем двумерную диаграмму рассеяния с кодировкой часов в виде цветов, чтобы лучше увидеть, как это представление отображает 24 часа суток в двумерное пространство, подобно 24-часовой версии аналоговых часов. Обратите внимание, что "25-й" час отображается обратно на 1-й час из-за периодической природы синусоидального/косинусоидального представления.

fig, ax = plt.subplots(figsize=(7, 5))
sp = ax.scatter(hour_df["hour_sin"], hour_df["hour_cos"], c=hour_df["hour"])
ax.set(
    xlabel="sin(hour)",
    ylabel="cos(hour)",
)
_ = fig.colorbar(sp)
plot cyclical feature engineering

Теперь мы можем построить конвейер извлечения признаков, используя эту стратегию:

cyclic_cossin_transformer = ColumnTransformer(
    transformers=[
        ("categorical", one_hot_encoder, categorical_columns),
        ("month_sin", sin_transformer(12), ["month"]),
        ("month_cos", cos_transformer(12), ["month"]),
        ("weekday_sin", sin_transformer(7), ["weekday"]),
        ("weekday_cos", cos_transformer(7), ["weekday"]),
        ("hour_sin", sin_transformer(24), ["hour"]),
        ("hour_cos", cos_transformer(24), ["hour"]),
    ],
    remainder=MinMaxScaler(),
)
cyclic_cossin_linear_pipeline = make_pipeline(
    cyclic_cossin_transformer,
    RidgeCV(alphas=alphas),
)
evaluate(cyclic_cossin_linear_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.125 +/- 0.014
Root Mean Squared Error: 0.166 +/- 0.020

Производительность нашей модели линейной регрессии с этой простой инженерией признаков немного лучше, чем при использовании исходных порядковых временных признаков, но хуже, чем при использовании one-hot кодированных временных признаков. Мы далее проанализируем возможные причины этого разочаровывающего результата в конце этой тетради.

Периодические сплайновые признаки#

Мы можем попробовать альтернативное кодирование периодических временных признаков с использованием сплайн-преобразований с достаточно большим количеством сплайнов, и как результат большим количеством расширенных признаков по сравнению с синусно-косинусным преобразованием:

from sklearn.preprocessing import SplineTransformer


def periodic_spline_transformer(period, n_splines=None, degree=3):
    if n_splines is None:
        n_splines = period
    n_knots = n_splines + 1  # periodic and include_bias is True
    return SplineTransformer(
        degree=degree,
        n_knots=n_knots,
        knots=np.linspace(0, period, n_knots).reshape(n_knots, 1),
        extrapolation="periodic",
        include_bias=True,
    )

Снова визуализируем эффект этого расширения признаков на некоторых синтетических данных о часах с небольшой экстраполяцией за пределы hour=23:

hour_df = pd.DataFrame(
    np.linspace(0, 26, 1000).reshape(-1, 1),
    columns=["hour"],
)
splines = periodic_spline_transformer(24, n_splines=12).fit_transform(hour_df)
splines_df = pd.DataFrame(
    splines,
    columns=[f"spline_{i}" for i in range(splines.shape[1])],
)
pd.concat([hour_df, splines_df], axis="columns").plot(x="hour", cmap=plt.cm.tab20b)
_ = plt.title("Periodic spline-based encoding for the 'hour' feature")
Periodic spline-based encoding for the 'hour' feature

Благодаря использованию extrapolation="periodic" параметр, мы наблюдаем, что кодирование признаков остается гладким при экстраполяции за пределы полуночи.

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

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

cyclic_spline_transformer = ColumnTransformer(
    transformers=[
        ("categorical", one_hot_encoder, categorical_columns),
        ("cyclic_month", periodic_spline_transformer(12, n_splines=6), ["month"]),
        ("cyclic_weekday", periodic_spline_transformer(7, n_splines=3), ["weekday"]),
        ("cyclic_hour", periodic_spline_transformer(24, n_splines=12), ["hour"]),
    ],
    remainder=MinMaxScaler(),
)
cyclic_spline_linear_pipeline = make_pipeline(
    cyclic_spline_transformer,
    RidgeCV(alphas=alphas),
)
evaluate(cyclic_spline_linear_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.097 +/- 0.011
Root Mean Squared Error: 0.132 +/- 0.013

Сплайн-признаки позволяют линейной модели успешно использовать периодические временные признаки и снизить ошибку с ~14% до ~10% от максимального спроса, что аналогично наблюдениям с one-hot кодированными признаками.

Качественный анализ влияния признаков на предсказания линейных моделей#

Здесь мы хотим визуализировать влияние выбора инженерии признаков на временную форму прогнозов.

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

naive_linear_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
naive_linear_predictions = naive_linear_pipeline.predict(X.iloc[test_0])

one_hot_linear_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
one_hot_linear_predictions = one_hot_linear_pipeline.predict(X.iloc[test_0])

cyclic_cossin_linear_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
cyclic_cossin_linear_predictions = cyclic_cossin_linear_pipeline.predict(X.iloc[test_0])

cyclic_spline_linear_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
cyclic_spline_linear_predictions = cyclic_spline_linear_pipeline.predict(X.iloc[test_0])

Мы визуализируем эти прогнозы, увеличив последние 96 часов (4 дня) тестового набора, чтобы получить качественные инсайты:

last_hours = slice(-96, None)
fig, ax = plt.subplots(figsize=(12, 4))
fig.suptitle("Predictions by linear models")
ax.plot(
    y.iloc[test_0].values[last_hours],
    "x-",
    alpha=0.2,
    label="Actual demand",
    color="black",
)
ax.plot(naive_linear_predictions[last_hours], "x-", label="Ordinal time features")
ax.plot(
    cyclic_cossin_linear_predictions[last_hours],
    "x-",
    label="Trigonometric time features",
)
ax.plot(
    cyclic_spline_linear_predictions[last_hours],
    "x-",
    label="Spline-based time features",
)
ax.plot(
    one_hot_linear_predictions[last_hours],
    "x-",
    label="One-hot time features",
)
_ = ax.legend()
Predictions by linear models

Из приведенного выше графика можно сделать следующие выводы:

  • The сырые порядковые временные признаки проблематичны, потому что они не учитывают естественную периодичность: мы наблюдаем большой скачок в предсказаниях в конце каждого дня, когда признак часа переходит с 23 обратно на 0. Можно ожидать аналогичных артефактов в конце каждой недели или года.

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

  • the периодические сплайн-признаки решают эти две проблемы одновременно: они дают линейной модели большую выразительность, позволяя фокусироваться на определенных часах благодаря использованию 12 сплайнов. Кроме того, extrapolation="periodic" опция обеспечивает плавное представление между hour=23 и hour=0.

  • The one-hot закодированные признаки ведут себя аналогично периодическим сплайн-признакам, но являются более острыми: например, они могут лучше моделировать утренний пик в будние дни, поскольку этот пик длится менее часа. Однако далее мы увидим, что то, что является преимуществом для линейных моделей, не обязательно является таковым для более выразительных моделей.

Мы также можем сравнить количество признаков, извлеченных каждым конвейером инженерии признаков:

naive_linear_pipeline[:-1].transform(X).shape
(17379, 19)
one_hot_linear_pipeline[:-1].transform(X).shape
(17379, 59)
cyclic_cossin_linear_pipeline[:-1].transform(X).shape
(17379, 22)
cyclic_spline_linear_pipeline[:-1].transform(X).shape
(17379, 37)

Это подтверждает, что стратегии кодирования one-hot и сплайнов создают гораздо больше признаков для представления времени, чем альтернативы, что, в свою очередь, дает последующей линейной модели больше гибкости (степеней свободы) для избежания недообучения.

Наконец, мы наблюдаем, что ни одна из линейных моделей не может аппроксимировать реальный спрос на аренду велосипедов, особенно для пиков, которые могут быть очень резкими в часы пик в рабочие дни, но гораздо более пологими в выходные: наиболее точные линейные модели на основе сплайнов или one-hot кодирования склонны прогнозировать пики аренды велосипедов, связанные с поездками на работу, даже в выходные дни и недооценивать события, связанные с поездками на работу, в рабочие дни.

Эти систематические ошибки прогнозирования выявляют форму недообучения и могут быть объяснены отсутствием членов взаимодействия между признаками, например, 'workingday' и признаков, производных от 'hours'. Эта проблема будет рассмотрена в следующем разделе.

Моделирование парных взаимодействий со сплайнами и полиномиальными признаками#

Линейные модели не захватывают автоматически эффекты взаимодействия между входными признаками. Не помогает то, что некоторые признаки являются маргинально нелинейными, как в случае с признаками, построенными с помощью SplineTransformer (или one-hot кодирование или бининг).

Однако возможно использовать PolynomialFeatures класс на грубо закодированных сплайнами часах для моделирования взаимодействия "рабочий день"/"часы" явно без введения слишком большого количества новых переменных:

from sklearn.pipeline import FeatureUnion
from sklearn.preprocessing import PolynomialFeatures

hour_workday_interaction = make_pipeline(
    ColumnTransformer(
        [
            ("cyclic_hour", periodic_spline_transformer(24, n_splines=8), ["hour"]),
            ("workingday", FunctionTransformer(lambda x: x == "True"), ["workingday"]),
        ]
    ),
    PolynomialFeatures(degree=2, interaction_only=True, include_bias=False),
)

Эти признаки затем объединяются с уже вычисленными в предыдущем конвейере на основе сплайнов. Мы можем наблюдать значительное улучшение производительности за счет явного моделирования этого попарного взаимодействия:

cyclic_spline_interactions_pipeline = make_pipeline(
    FeatureUnion(
        [
            ("marginal", cyclic_spline_transformer),
            ("interactions", hour_workday_interaction),
        ]
    ),
    RidgeCV(alphas=alphas),
)
evaluate(cyclic_spline_interactions_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.078 +/- 0.009
Root Mean Squared Error: 0.104 +/- 0.009

Моделирование нелинейных взаимодействий признаков с помощью ядер#

Предыдущий анализ подчеркнул необходимость моделирования взаимодействий между "workingday" и "hours". Другой пример такого нелинейного взаимодействия, которое мы хотели бы смоделировать, может быть влияние дождя, которое может быть неодинаковым в рабочие дни и в выходные и праздничные дни, например.

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

В качестве альтернативы мы можем использовать метод Нюстрёма для вычисления приближенного разложения полиномиального ядра. Попробуем последнее:

from sklearn.kernel_approximation import Nystroem

cyclic_spline_poly_pipeline = make_pipeline(
    cyclic_spline_transformer,
    Nystroem(kernel="poly", degree=2, n_components=300, random_state=0),
    RidgeCV(alphas=alphas),
)
evaluate(cyclic_spline_poly_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.053 +/- 0.002
Root Mean Squared Error: 0.076 +/- 0.004

Мы наблюдаем, что эта модель почти может соперничать с производительностью градиентного бустинга деревьев со средней ошибкой около 5% от максимального спроса.

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

Для полноты картины мы также оцениваем комбинацию one-hot кодирования и аппроксимации ядра:

one_hot_poly_pipeline = make_pipeline(
    ColumnTransformer(
        transformers=[
            ("categorical", one_hot_encoder, categorical_columns),
            ("one_hot_time", one_hot_encoder, ["hour", "weekday", "month"]),
        ],
        remainder="passthrough",
    ),
    Nystroem(kernel="poly", degree=2, n_components=300, random_state=0),
    RidgeCV(alphas=alphas),
)
evaluate(one_hot_poly_pipeline, X, y, cv=ts_cv)
Mean Absolute Error:     0.082 +/- 0.006
Root Mean Squared Error: 0.111 +/- 0.011

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

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

gbrt.fit(X.iloc[train_0], y.iloc[train_0])
gbrt_predictions = gbrt.predict(X.iloc[test_0])

one_hot_poly_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
one_hot_poly_predictions = one_hot_poly_pipeline.predict(X.iloc[test_0])

cyclic_spline_poly_pipeline.fit(X.iloc[train_0], y.iloc[train_0])
cyclic_spline_poly_predictions = cyclic_spline_poly_pipeline.predict(X.iloc[test_0])

Снова увеличим масштаб на последние 4 дня тестового набора:

last_hours = slice(-96, None)
fig, ax = plt.subplots(figsize=(12, 4))
fig.suptitle("Predictions by non-linear regression models")
ax.plot(
    y.iloc[test_0].values[last_hours],
    "x-",
    alpha=0.2,
    label="Actual demand",
    color="black",
)
ax.plot(
    gbrt_predictions[last_hours],
    "x-",
    label="Gradient Boosted Trees",
)
ax.plot(
    one_hot_poly_predictions[last_hours],
    "x-",
    label="One-hot + polynomial kernel",
)
ax.plot(
    cyclic_spline_poly_predictions[last_hours],
    "x-",
    label="Splines + polynomial kernel",
)
_ = ax.legend()
Predictions by non-linear regression models

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

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

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

Мы также наблюдаем, что ни одна из моделей не может успешно предсказать некоторые пиковые аренды в часы пик в рабочие дни. Возможно, для дальнейшего повышения точности прогнозов потребуется доступ к дополнительным признакам. Например, может быть полезно иметь доступ к географическому распределению парка в любой момент времени или к доле велосипедов, которые обездвижены из-за необходимости обслуживания.

Давайте наконец посмотрим более количественно на ошибки предсказания этих трех моделей, используя графики рассеяния истинного и предсказанного спроса:

from sklearn.metrics import PredictionErrorDisplay

fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(13, 7), sharex=True, sharey="row")
fig.suptitle("Non-linear regression models", y=1.0)
predictions = [
    one_hot_poly_predictions,
    cyclic_spline_poly_predictions,
    gbrt_predictions,
]
labels = [
    "One hot +\npolynomial kernel",
    "Splines +\npolynomial kernel",
    "Gradient Boosted\nTrees",
]
plot_kinds = ["actual_vs_predicted", "residual_vs_predicted"]
for axis_idx, kind in enumerate(plot_kinds):
    for ax, pred, label in zip(axes[axis_idx], predictions, labels):
        disp = PredictionErrorDisplay.from_predictions(
            y_true=y.iloc[test_0],
            y_pred=pred,
            kind=kind,
            scatter_kwargs={"alpha": 0.3},
            ax=ax,
        )
        ax.set_xticks(np.linspace(0, 1, num=5))
        if axis_idx == 0:
            ax.set_yticks(np.linspace(0, 1, num=5))
            ax.legend(
                ["Best model", label],
                loc="upper center",
                bbox_to_anchor=(0.5, 1.3),
                ncol=2,
            )
        ax.set_aspect("equal", adjustable="box")
plt.show()
Non-linear regression models

Эта визуализация подтверждает выводы, сделанные на предыдущем графике.

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

Заключительные замечания#

Отметим, что мы могли бы получить немного лучшие результаты для ядерных моделей, используя больше компонентов (более высокий ранг аппроксимации ядра) за счет более длительного времени подгонки и прогнозирования. Для больших значений n_components, производительность признаков с one-hot кодированием могла бы даже соответствовать сплайновым признакам.

The Nystroem + RidgeCV регрессор также можно было бы заменить на MLPRegressor с одним или двумя скрытыми слоями и мы получили бы довольно похожие результаты.

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

Наконец, в этом ноутбуке мы использовали RidgeCV поскольку он очень эффективен с вычислительной точки зрения. Однако он моделирует целевую переменную как гауссовскую случайную величину с постоянной дисперсией. Для задач положительной регрессии, вероятно, использование распределения Пуассона или Гамма было бы более логичным. Этого можно достичь, используя GridSearchCV(TweedieRegressor(power=2), param_grid({"alpha": alphas})) вместо RidgeCV.

Общее время выполнения скрипта: (0 минут 16.827 секунд)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_cyclical_feature_engineering.ipynb

Download Python source code: plot_cyclical_feature_engineering.py

Download zipped: plot_cyclical_feature_engineering.zip

Связанные примеры

Лаггированные признаки для прогнозирования временных рядов

Лаггированные признаки для прогнозирования временных рядов

Поддержка категориальных признаков в градиентном бустинге

Поддержка категориальных признаков в градиентном бустинге

Полиномиальная и сплайновая интерполяция

Полиномиальная и сплайновая интерполяция

Сравнение Target Encoder с другими кодировщиками

Сравнение Target Encoder с другими кодировщиками

Галерея, созданная Sphinx-Gallery

предыдущий

Моделирование распределения видов

next

Извлечение тем с использованием неотрицательной матричной факторизации и латентного размещения Дирихле

На этой странице
  • Исследование данных на наборе Bike Sharing Demand
  • Кросс-валидация на основе времени
  • Градиентный бустинг
  • Наивная линейная регрессия
  • Временные шаги как категории
  • Тригонометрические признаки
  • Периодические сплайновые признаки
  • Качественный анализ влияния признаков на предсказания линейных моделей
  • Моделирование парных взаимодействий со сплайнами и полиномиальными признаками
  • Моделирование нелинейных взаимодействий признаков с помощью ядер
  • Заключительные замечания

Эта страница

  • Показать исходный код
Скачать исходный код
Скачать Jupyter notebook
Скачать в архиве
Launch JupyterLite
Launch binder

© Авторские права 2007 - 2025, разработчики scikit-learn (лицензия BSD).