2025, Nov 08 21:02
Извлечение полиномиального уравнения из Ridge в Pipeline scikit-learn
Показываем, как получить явное полиномиальное уравнение из модели PolynomialFeatures+Ridge в scikit-learn: где взять coef_ и intercept_, и как учесть Pipeline.
Когда вы обучаете полиномиальную регрессию Ridge в scikit-learn и настраиваете её через GridSearchCV, модель может работать отлично, но превратить её в «обычное» полиномиальное уравнение вида ax^3 + bx^2 + cx + d не так просто. Одних только найденных степени и alpha недостаточно; нужны ещё подгоняемые коэффициенты, которые задают итоговую формулу. Важно понимать: внутри Pipeline каждый шаг преобразует данные, поэтому коэффициенты нужно брать из финального оценщика и не забывать применять те же преобразования, если вы собираетесь воспроизводить предсказания вне scikit-learn.
Минимальная настройка, из которой возникает вопрос
Ниже — типичный конвейер: он масштабирует признаки, расширяет их до полиномиальных и обучает модель Ridge. В сетке перебора варьируются степень полинома и параметр alpha у Ridge.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
model_flow = Pipeline([
('scale', StandardScaler()),
('poly_map', PolynomialFeatures()),
('reg', Ridge())
])
search_grid = {
'poly_map__degree': [2, 3],
'reg__alpha': [0.01, 0.1, 1]
}
run_eval('Polynomial Ridge', model_flow, search_grid, linear_mode=False)
После нахождения лучшей конфигурации логично захотеть «вывести уравнение». Возникает вопрос: где взять a, b, c, d и как корректно использовать их за пределами scikit-learn?
Что на самом деле происходит в конвейере
Pipeline последовательно связывает преобразования. Выход StandardScaler поступает на вход PolynomialFeatures; расширенная матрица затем передаётся в Ridge. Если вы извлечёте коэффициенты, но на этапе инференса пропустите часть шагов, предсказания не совпадут с обученной моделью. Поэтому коэффициенты имеют смысл только при условии, что перед вычислением полинома вы применяете к входам те же самые предобработки.
Оценённые параметры находятся в двух местах. Коэффициенты при признаках — в coef_, а вертикальный сдвиг — в intercept_. Такое разделение действует и для линейной, и для полиномиальной регрессии. Распространённая ловушка — думать, что первый элемент coef_ является свободным членом; это не так. На практике свободный член добавляют явно через intercept_ при записи итогового уравнения.
Как извлечь полиномиальное уравнение
Пример ниже генерирует синтетические данные из кубического полинома с шумом, обучает связку PolynomialFeatures + Ridge, а затем восстанавливает предсказание с помощью coef_ и intercept_. Он также показывает, что первый элемент coef_ не следует считать свободным членом — его нужно добавлять отдельно.
import warnings
warnings.filterwarnings('ignore')
import numpy as np
import pandas as pd
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import Ridge
from sklearn.preprocessing import PolynomialFeatures
true_coefs = [0, 10, -20, 0.30]
def make_target(u, c):
jitter = np.random.uniform(0, 50E6)
return (c[0] + c[1]*u + c[2]*u**2 + c[3]*u**3 + jitter)
xs = range(0, 1000, 50)
frame_synth = pd.DataFrame({'x': xs})
frame_synth['y'] = frame_synth['x'].apply(lambda z: make_target(z, true_coefs))
X_in = frame_synth[['x']]
y_in = frame_synth[['y']]
poly_ridge = make_pipeline(PolynomialFeatures(degree=3), Ridge(alpha=0, solver='cholesky'))
model_fit = poly_ridge.fit(X_in, y_in)
y_hat = model_fit.predict(X_in)
w = poly_ridge.steps[1][1].coef_
b = poly_ridge.steps[1][1].intercept_
print('coef:', w, '\nbias:', b)
def eval_poly(u, coeffs):
return (coeffs[0] + coeffs[1]*u + coeffs[2]*u**2 + coeffs[3]*u**3 + b)
recon = eval_poly(X_in, w).rename(columns={'x': 'y'})
resid = y_hat - recon.y
Конвейер обучает PolynomialFeatures степени 3 и регрессию Ridge. После обучения коэффициенты берутся из последнего шага. Свободный член — отдельно. При восстановлении предсказания intercept_ добавляется явно, при этом первый элемент coef_, который на первый взгляд может казаться свободным членом, остаётся частью набора коэффициентов. Это повторяет поведение обученной модели.
Как применять это вне scikit-learn
Если вы обучались с Pipeline, воспроизводите все шаги, когда рассчитываете предсказания в другом окружении. Масштабировали признаки и расширяли их PolynomialFeatures во время обучения — сделайте то же самое перед применением коэффициентов. Пропустите хотя бы одно преобразование — и коэффициенты перестанут соответствовать «сырым» входам, а ваши расчёты разойдутся с тем, что выдаёт модель в Python.
Почему это важно
Представлять модель в виде «простого уравнения» удобно для небольших модулей, встраиваемых скриптов или переноса в другие языки. Но в случае с конвейерами «уравнение» — это композиция преобразований плюс финальная линейная модель. Понимание, что coef_ хранит веса, а intercept_ — это свободный член, помогает извлечь точные значения, которые определяют предсказания, и воспроизвести результат там, где он вам нужен.
Вывод
Чтобы превратить полиномиальную модель Ridge в явное уравнение, возьмите коэффициенты из coef_ и добавьте intercept_ при вычислении. Если обучение шло внутри Pipeline, воспроизведите шаги предобработки на этапе инференса; иначе значения не сойдутся. При соблюдении этих условий вы сможете перенести выученный полином в любую среду, где доступны те же преобразования и арифметика.
Статья основана на вопросе на StackOverflow от MikeB2019x и ответе от MikeB2019x.