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

У всех нас был какой-то опыт линейной регрессии. Это один из наиболее часто используемых методов регрессии. Почему? Потому что это просто объяснить и легко реализовать. Но что происходит, когда у вас более одной переменной? Как вы можете справиться с этой повышенной сложностью и по-прежнему использовать такую ​​простую для понимания регрессию? А что будет, если система будет еще сложнее? Представим, что у вас есть взаимодействие между двумя переменными.

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

Если вы хотите освежить в памяти линейную регрессию, есть много доступных ресурсов, и я также написал краткое введение в кодирование. А как насчет символической регрессии? В этой статье мы будем использовать gplearn. См. Его документацию для получения дополнительной информации или, если хотите, прочтите мою другую статью о том, как использовать его со сложными функциями в Python здесь.

Подготовка данных

Мы рассмотрим два варианта использования регрессии. В первом случае у нас будет всего четыре переменных (от x1 до x4), которые складываются плюс некоторые предопределенные взаимодействия: x1 * x2, x3 * x2 и x4 * x2.

Обратите внимание, что в нашем наборе данных out_df нет условий взаимодействия. То, что мы будем делать, будет пытаться обнаружить эти отношения с помощью наших инструментов. Вот как выглядят переменные, когда мы строим их с помощью seaborn, используя x4 в качестве оттенка (рисунок 1):

Y во втором случае (рисунок 2) определяется как:

y_true = x1+x2+x3+x4+ (x1*x2)*x2 - x3*x2 + x4*x2*x3*x2 + x1**2

Довольно сложный сценарий!

Случай 1: множественная линейная регрессия

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

import statsmodels.api as sm 
Xb = sm.add_constant(out_df[['x1','x2','x3','x4']])
mod = sm.OLS(y_true, Xb)
res = mod.fit()
res.summary()

Ой, это явно не тот результат, на который мы надеялись. R² составляет всего 0,567, и, более того, я удивлен, увидев, что значение P для x1 и x4 невероятно велико. Нам нужна другая стратегия.

Полиномиальные особенности

Что мы можем сделать, так это импортировать библиотеку Python под названием PolynomialFeatures из sklearn, которая будет генерировать полиномиальные и интерактивные функции. Например, если входной образец является двумерным и имеет форму [a, b], полиномиальные характеристики степени 2 будут [1, a, b, a², ab, b²].

from sklearn.preprocessing import PolynomialFeatures
import scipy.special
poly = PolynomialFeatures(interaction_only=True)
X_tr = poly.fit_transform(Xb)
Xt = pd.concat([Xb,pd.DataFrame(X_tr,columns=poly.get_feature_names()).drop([‘1’,’x0',’x1',’x2',’x3',’x4'],1)],1)

С «Interaction_only = True» создаются только интерактивные функции: функции, которые являются продуктами не более degree отдельных входных функций (но не x[1] ** 2, x[0] * x[2] ** 3 и т. Д.). Параметр степени по умолчанию - 2.

С тем же кодом, что и раньше, но с использованием Xt сейчас, дает следующие результаты.

mod = sm.OLS(y_true, Xt)
res = mod.fit()
res.summary()

Теперь R² на рисунке 4 равен 1, что идеально. Слишком идеально, чтобы быть хорошим? На самом деле терминов взаимодействия в сводной статистике очень много. Некоторые из них мы даже не знали. Наше уравнение имеет вид:
y = x₁ + 05 * x₂ + 2 * x₃ + x₄ + x₁ * x₂ - x₃ * x₂ + x₄ * x₂
Итак, наша подгонка вводит взаимодействия, которых мы не делали » t явно использовать в нашей функции. Даже если мы удалим те, которые имеют высокое значение p (x₁ x₄), у ​​нас останется сложный сценарий. Это может быть проблемой для обобщения. Мы можем использовать генетическое программирование, чтобы дать нам здесь несколько советов.

Генетическое программирование: GPlearn

С помощью генетического программирования мы в основном говорим системе сделать все возможное, чтобы найти взаимосвязи в наших данных в аналитической форме. Если вы прочитаете другой учебник, некоторые функции, которые я здесь буду называть, будут более понятными. Однако в основном мы хотим импортировать SymbolicRegressor из gplearn.genetic, и мы будем использовать sympy для красивого форматирования наших уравнений.
Поскольку мы находимся в процессе, мы также импортируем регрессоры RandomForest и DecisionTree, чтобы позже сравнить результаты между всеми этими инструментами. Ниже кода, чтобы заставить его работать:

Словарь конвертера предназначен для того, чтобы помочь нам сопоставить уравнение с соответствующей функцией Python, чтобы simpy выполнял свою работу. Мы также разделяем наши данные на train_test, чтобы сравнивать наши прогнозы только на тестовых данных. Мы определили набор функций, в котором мы используем стандартные функции из набора gplearn.
На 40-м поколении код останавливается, и мы видим, что R² почти равно 1, а сгенерированная формула теперь довольно легко читается.

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

y = -x₃ (x₂–2) + x₂ (x₁+x₄+ 0.5)+x₁+x₄

Все алгоритмы хорошо справились с этой работой: вот R².

statsmodels OLS with polynomial features 1.0, 
random forest 0.9964436147653762, 
decision tree 0.9939005077996459, 
gplearn regression 0.9999946996993035

Случай 2: взаимодействия 2-го порядка

В этом случае отношения усложняются по мере увеличения порядка взаимодействия:

X = np.column_stack((x1, x2, x3, x4))
y_true = x1+x2+x3+x4+ (x1*x2)*x2 - x3*x2 + x4*x2*x3*x2 + x1**2
out_df['y'] = y_true

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

poly = PolynomialFeatures(interaction_only=True)
X_tr = poly.fit_transform(out_df.drop('y',1))
Xt = pd.concat([out_df.drop('y',1),pd.DataFrame(X_tr,columns=poly.get_feature_names()).drop(['1','x0','x1','x2','x3'],1)],1)
Xt = sm.add_constant(Xt)
mod = sm.OLS(y_true, Xt)
res = mod.fit()
res.summary()

В этом сценарии наш подход больше не приносит результатов. Понятно, что в нашем наборе данных нет правильных предикторов. Мы могли бы использовать полиномиальные особенности для исследования взаимодействий более высокого порядка, но размерность, вероятно, слишком сильно увеличится, и у нас останется не намного больше знаний, чем раньше. Кроме того, если бы у вас был реальный набор данных и вы не знали формулу цели, вы бы увеличили порядок взаимодействий? Думаю, нет!

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

X_train, X_test, y_train, y_test = train_test_split(out_df.drop('y',1), y, test_size=0.30, random_state=42)
est_tree = DecisionTreeRegressor(max_depth=5)
est_tree.fit(X_train, y_train)
est_rf = RandomForestRegressor(n_estimators=100,max_depth=5)
est_rf.fit(X_train, y_train)
y_gp = est_gp.predict(X_test)
score_gp = est_gp.score(X_test, y_test)
y_tree = est_tree.predict(X_test)
score_tree = est_tree.score(X_test, y_test)
y_rf = est_rf.predict(X_test)
score_rf = est_rf.score(X_test, y_test)
y_sm = res.predict(Xt)

est_gp.fit(X_train, y_train)
print('R2:',est_gp.score(X_test,y_test))
next_e = sympify((est_gp._program), locals=converter)
next_e

Результат невероятный: снова после 40 поколений у нас остается невероятно высокий R² и, что еще лучше, простое аналитическое уравнение.

Исходная формула такая:

Итак, мы видим, что действительно существуют различия в терминах, которые включают x1 и его взаимодействия. А термины, не зависящие от него, совершенно там. Тем не менее, по сравнению с подходом полиномиальных функций, здесь мы имеем дело с гораздо менее сложной формулой.

В чем ошибка разных систем? Что ж, для gplearn это невероятно мало по сравнению с другими. На рисунке 8 показана ошибка координаты y по сравнению с фактическим значением y. Хотя ось x является общей, вы можете заметить, насколько разной стала ось y. Максимальная ошибка с GPlearn составляет около 4, в то время как другие методы могут отображать всплески до 1000.

Заключение

В первой части этой статьи мы увидели, как бороться с множественной линейной регрессией при наличии взаимодействий. Мы использовали statsmodels OLS для множественной линейной регрессии и функции многочлена sklearn для генерации взаимодействий. Затем мы подошли к той же проблеме с другим классом алгоритмов, а именно с генетическим программированием, которое легко импортировать и реализовать и которое дает аналитическое выражение.

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

использованная литература

[1] статистические модели
[2] особенности полинома sklearn
[3] gplearn