Прекратите подгонять прямые линии к криволинейным данным. Более разумный способ обработки нелинейностей.
Делиться

Всем известно, что линейные модели могут быть… ну, довольно жёсткими. Вы когда-нибудь смотрели на диаграмму рассеяния и понимали, что прямая линия просто не подойдёт? Все мы через это проходили.
Работа с реальными данными всегда сопряжена с трудностями. Чаще всего кажется, что исключение становится правилом. Данные, которые вы получаете на работе, совсем не похожи на те прекрасные линейные наборы данных, которые мы использовали в течение многих лет обучения в академии.
Например, вы смотрите на график «Зависимость потребления энергии от температуры». Это не линия, а кривая. Обычно первым делом мы пытаемся использовать полиномиальную регрессию. Но это ловушка!
Если вы когда-либо видели, как кривая модели выходит из-под контроля на краях графика, вы стали свидетелем «феномена Ранге». Полиномы высоких степеней похожи на малыша с цветным карандашом: они слишком гибкие и недисциплинированные.
Поэтому я собираюсь показать вам вариант, называемый сплайнами. Это отличное решение: более гибкое, чем прямая линия, но гораздо более дисциплинированное, чем полином.
Сплайны — это математические функции, определяемые полиномами и используемые для сглаживания кривой.
Вместо того чтобы пытаться подогнать все данные под одно сложное уравнение, вы разбиваете данные на сегменты в точках, называемых узлами . Каждому сегменту присваивается свой простой многочлен, и все они соединяются настолько плавно, что швы даже не видны.
Проблема многочленов
Представьте, что у нас есть нелинейный тренд, и мы применяем к нему полином x² или x³. Локально всё выглядит нормально, но затем мы смотрим на края данных, и кривая сильно отклоняется. Согласно феномену Рунге [2] , у полиномов высокой степени есть такая проблема: одна странная точка данных на одном конце может вывести всю кривую из равновесия на другом конце.

Почему шлицы — это «идеальный» выбор
Сплайны не пытаются подогнать все данные под одно гигантское уравнение. Вместо этого они делят ваши данные на сегменты с помощью точек, называемых узлами . Использование узлов имеет ряд преимуществ.
- Локальный контроль: то, что происходит в одном сегменте, остается в этом сегменте. Поскольку эти фрагменты являются локальными, странная точка данных на одном конце вашего графика не испортит соответствие на другом конце.
- Плавность: Для обеспечения идеальной плавности кривой в местах пересечения сегментов используются «B-сплайны» (базисные сплайны).
- Устойчивость: В отличие от полиномов, они не выходят из-под контроля на границах.
Хорошо. Достаточно разговоров, давайте теперь реализуем это решение.
Реализация с помощью Scikit-Learn
Для этой цели лучше всего подходит SplineTransformer из библиотеки Scikit-Learn. Он преобразует одну числовую характеристику в несколько базисных характеристик, которые простая линейная модель может затем использовать для изучения сложных нелинейных форм.
Давайте импортируем несколько модулей.
import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import SplineTransformer from sklearn.linear_model import Ridge from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV
Далее мы создаём данные в виде криволинейных колебаний.
# 1. Создайте синтетические данные с «извилистым» графиком (например, сезонные продажи) rng = np.random.RandomState(42) X = np.sort(rng.rand(100, 1) * 10, axis=0) y = np.sin(X).ravel() + rng.normal(0, 0.1, X.shape[0]) # Постройте график данных plt.figure(figsize=(12, 5)) plt.scatter(X, y, color='gray', alpha=0.5, label='Data') plt.legend() plt.title(«Data») plt.show()

Хорошо. Теперь мы создадим конвейер, который запустит SplineTranformer с настройками по умолчанию, а затем Ridge Regression.
# 2. Создание конвейера: Сплайны + Линейная модель # n_knots=5 (по умолчанию) создает 4 сегмента; degree=3 делает его кубическим сплайном model = make_pipeline( SplineTransformer(n_knots=5, degree=3), Ridge(alpha=0.1) )
Далее мы настроим количество узлов для нашей модели. Мы используем GridSearchCV для запуска нескольких версий модели, тестируя различное количество узлов, пока не найдем ту, которая лучше всего работает на наших данных.
# Мы настраиваем 'n_knots', чтобы найти оптимальную настройку param_grid = {'splinetransformer__n_knots': range(3, 12)} grid = GridSearchCV(model, param_grid, cv=5) grid.fit(X, y) print(f»Оптимальное количество узлов: {grid.best_params_['splinetransformer__n_knots']}») Оптимальное количество узлов: 8
Затем мы переобучаем нашу сплайновую модель с оптимальным количеством узлов, делаем прогноз и строим график данных. Давайте также разберемся, что мы здесь делаем, с помощью краткого описания аргументов класса SplineTransformer:
- n_knots: количество узлов в кривой. Чем больше узлов, тем гибче кривая.
- Степень: Этот параметр определяет «гладкость» сегментов. Он относится к степени полинома, используемого между узлами (1 — прямая линия; 2 — более гладкая линия; 3 — значение по умолчанию).
- Узлы: Этот параметр указывает модели, где размещать точки соединения. Например, параметр uniform разделяет кривую на равные участки, а параметр quantile выделяет больше узлов там, где данные более плотные.
- Совет : используйте параметр 'квантиль', если ваши данные сгруппированы.
- Экстраполяция: указывает модели, что она должна делать, когда сталкивается с данными, выходящими за пределы диапазона, который она видела во время обучения.
- Совет : используйте слово «периодический» для циклических данных, таких как календарные или часовые метки.
- include_bias: Следует ли включать столбец «смещение» (столбец, состоящий из одних единиц). Если вы используете модель линейной регрессии или гребневой регрессии на более поздних этапах обработки данных, эти модели обычно имеют собственный параметр fit_intercept=True, поэтому вы часто можете установить его значение в False, чтобы избежать избыточности.
# 2. Создание оптимизированной сплайновой модели = make_pipeline( SplineTransformer(n_knots=8, degree=3, knots= 'uniform', extrapolation='constant', include_bias=False), Ridge(alpha=0.1) ).fit(X, y) # 3. Прогнозирование и визуализация y_plot = model.predict(X) # Построение графика plt.figure(figsize=(12, 5)) plt.scatter(X, y, color='gray', alpha=0.5, label='Data') plt.plot(X, y_plot, color='teal', linewidth=3, label='Spline Model') plt.plot(X, y_plot_10, color='purple', linewidth=2, label='Polynomial Fit (Degree 20)') plt.legend() plt.title(«Сплайны: гибкие, но дисциплинированные») plt.show()
Вот результат. Использование сплайнов обеспечивает лучший контроль и более гладкую модель, позволяя избежать проблем на концах.

Мы сравниваем полиномиальную модель степени 20 со сплайновой моделью. Можно утверждать, что модели меньших степеней лучше подходят для моделирования этих данных, и это будет справедливо. Я протестировал модели до 13-й степени, и они хорошо согласуются с этим набором данных.
Однако именно в этом и заключается суть данной статьи. Когда модель плохо соответствует данным, и нам приходится постоянно увеличивать степень полинома, мы неизбежно столкнемся с проблемой «диких границ».
Применение в реальной жизни
Где это можно реально использовать в бизнесе?
- Циклы временных рядов: используйте extrapolation='periodic' для таких параметров, как «час дня» или «месяц года». Это гарантирует, что модель будет знать, что 23:59 находится непосредственно рядом с 00:01. С помощью этого аргумента мы указываем SplineTransformer, что конец нашего цикла (час 23) должен замыкаться и совпадать с началом (час 0). Таким образом, сплайн гарантирует, что наклон и значение в конце дня идеально совпадают с началом следующего дня.
- Дозозависимый эффект в медицине: моделирование воздействия лекарства на пациента. Большинство лекарств следуют нелинейной кривой, где польза в конечном итоге стабилизируется (насыщение) или, что еще хуже, переходит в токсичность. Сплайны являются здесь «золотым стандартом», поскольку они позволяют отображать эти сложные биологические изменения, не загоняя данные в жесткую форму.
- Доход против опыта: Зарплата часто быстро растет на начальном этапе, а затем стабилизируется; сплайны идеально отражают этот «изгиб».
Прежде чем уйти
Мы многое здесь обсудили, от того, почему полиномы могут быть «нестандартным» выбором, до того, как периодические сплайны решают проблему «полуночного разрыва». Вот краткий обзор, который стоит запомнить:
- Золотое правило: используйте сплайны, когда прямая линия слишком проста, но полином высокой степени начинает колебаться и приводить к переобучению.
- Узлы — это ключ: Узлы — это «соединения» вашей модели. Правильное определение их количества с помощью GridSearchCV — это разница между плавной кривой и зазубренным беспорядком.
- Периодическая мощность: Для любой переменной, изменяющейся циклически (часы, дни, месяцы), используйте параметр extrapolation='periodic'. Это гарантирует, что модель понимает, что конец цикла плавно возвращается в начало.
- Разработка признаков > Сложные модели: Зачастую простая регрессия Риджа в сочетании со SplineTransformer превосходит сложную модель типа «черный ящик», при этом ее гораздо проще объяснить начальнику.
Если вам понравился этот материал, вы можете узнать больше о моей работе и контактных данных на моем сайте.
https://gustavorsantos.me
Репозиторий GitHub
Вот полный код этого упражнения, а также несколько дополнительных фрагментов.
https://github.com/gurezende/Studying/blob/master/Python/sklearn/SplineTransformer.ipynb
Ссылки
[1. Документация по SplineTransformer] https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.SplineTransformer.html
[2. Феномен Рунге] https://en.wikipedia.org/wiki/Runge%27s_phenomenon
[3. Документация по MakePipeline] https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.make_pipeline.html
Источник: towardsdatascience.com



























