Улучшение линейной регрессии

На прошлой неделе я опубликовал блог, в котором подробно описал все этапы процесса моделирования линейной регрессии. В этом посте мы немного изменим данные, чтобы уменьшить показатели результатов нашей модели. Затем мы рассмотрим самый важный шаг в любой линейной регрессии: конструирование функций . Весь код можно найти в этой записной книжке. Этот блог начинается в середине блокнота с заголовка Разработка функций.

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

Обзор оригинальной модели

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

  • Продажи (целевые): штучные продажи в каждом месте
  • CompPrice: цена, взимаемая ближайшим конкурентом в каждом месте.
  • Доход: уровень дохода сообщества
  • Реклама: местный рекламный бюджет компании в каждом месте
  • Население: численность населения региона (в тысячах).
  • Цена: цена, взимаемая за автокресло на каждой площадке.
  • ShelveLoc: качество размещения стеллажей на площадке (хорошее | плохое | среднее)
  • Возраст: средний возраст местного населения.
  • Образование: уровень образования в каждом месте
  • Городской: находится ли магазин в городе или в деревне.
  • США: находится ли магазин в США или нет

Подход, который мы использовали, позволил нам собрать воедино хорошо работающую модель, которая вернула метрику r-квадрат 0,88 со среднеквадратичной ошибкой 0,96. Обе эти метрики указывают на очень сильную модель, но мы должны помнить, что эти данные были созданы исключительно для целей упражнения и не являются данными реального мира. Это было бы хорошо и избавило бы от множества головных болей, но очень редко реальные данные приводят к столь сильному результату после первой итерации модели.

Маленький шаг назад

Прежде чем мы перейдем к разработке функций, давайте сначала взглянем на наши предыдущие данные регрессии. Поскольку у нас была такая сильная регрессия производительности, ради цели этого блога мы хотим удалить наиболее сильно влияющую переменную на нашу целевую переменную. Ковариация измеряет, как две переменные будут отличаться друг от друга (масштаб = [0, 1]). Если ковариация между переменной X и переменной Y высока, это означает, что две переменные оказывают сильное влияние друг на друга. Блок кода ниже покажет нам, как увидеть ковариацию между всеми переменными в нашей обучающей выборке модели:

X_train_prep.cov()

Этот простой метод выведет вышеуказанный DataFrame, который чрезвычайно информативен, но также требует большого объема для усвоения. Мы считываем переменные по оси Y и находим соответствующие показатели ковариации под каждой из переменных по оси X. Мы пытаемся определить, какая переменная имеет наибольшее влияние на другие переменные, чтобы мы могли удалить их из этой модели. Повторюсь, мы выявляем и удаляем только важные переменные, чтобы снизить производительность нашей модели в целях разработки функций. В реальной модели это было бы совершенно нелепо без обоснования знания предметной области.

Мы можем сделать вывод ковариации немного более удобоваримым, визуализировав его на морской тепловой карте.

sns.heatmap(X_train_prep.cov())

Тепловая карта просто показывает наши степени ковариации через интенсивность цвета. Чем светлее цвет, тем выше ковариация двух переменных (по шкале в правой части карты). Мы видим, что CompPrice оказывает сильное влияние на себя и на цену. По сути, это имеет смысл, что изменение цены конкурента повлияет на изменение в исходном бизнесе, изменив его цену для отражения. Опять же, именно здесь так важно знание предметной области. Поскольку у нас есть две очень похожие прогнозные переменные в «CompPrice» и «Price», а «CompPrice» - это переменная, не присущая бизнесу, продающему автокресла, мы удалим «CompPrice», чтобы ухудшить нашу модель.

X_train_prep.drop('CompPrice', axis = 1, inplace = True)
lr2 = LinearRegression()
lr2.fit(X_train_prep, y_train)
y_hat_train = lr2.predict(X_train_prep)
r2_score(y_train, y_hat_train)
  • Оценка R-квадрат = 0,7343

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

Конструирование признаков - двумерные комбинации

Теперь, когда мы удалили CompPrice из нашего DataFrame, он выглядит следующим образом (данные уже масштабированы и нормализованы):

Во время разработки функций мы хотим попытаться создать широкий спектр взаимодействий между несколькими переменными, чтобы создать новые переменные. Например, умножение цены на доход или реклама по городскому населению. Управляя ими вместе, мы создаем возможности для появления новых и эффективных функций, которые потенциально могут повлиять на нашу целевую переменную, таким образом проектируя наши функции. Для этого аргумента мы создадим столько же двумерных комбинаций наших прогнозных переменных, используя метод «комбинаций» из библиотеки itertools.

from itertools import combinations
columns_list = X_train_prep.columns
interactions = list(combinations(column_list, 2))

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

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

interaction_dict = {}
for interaction in interactions:
   X_train_int = X_train_prep
   X_train_int['int'] = X_train_int[interaction[0]] * X_train_int[interaction[1]]
   lr3 = LinearRegression()
   lr3.fit(X_train_int, y_train)
   interaction_dict[lr3.score(X_train_int, y_train)] = interaction

В приведенном выше блоке кода мы устанавливаем X_train в новую переменную копии, устанавливаем новую функцию, 'int', умножая две функции вместе, а затем используем этот новый DataFrame, содержащий новую функцию комбинирования, как единственные аргументы в пользу нашей новой LinearRegression (исключая исходные функции, которые уже находятся в нашем DataFrame).

Теперь у нас фактически завершены все наши новые регрессии, благодаря итерации, но мы не можем их видеть, потому что все они хранятся в «Interaction_dict». Давайте отсортируем словарь, чтобы получить пять наиболее эффективных (оценка r-квадрата) комбинаций.

top_5 = sorted(interaction_dict.keys(), reverse = True)[:5]
for interaction in top_5:
   print(interaction_dict[interaction])

Наши самые эффективные комбинации:

  • Реклама x Образование
  • Реклама x США
  • США x реклама
  • Цена x Возраст
  • Хороший x возраст

На этом этапе знание предметной области поднимает его на еще более высокую ступень. Переменные, возвращаемые здесь, должны начать заставлять ваш мозг ломать голову над тем, почему определенные переменные более эффективны. Проявите творческий подход к этому, в конце концов, мы занимаемся разработкой. Ради этого блога предположим, что наши знания предметной области уже предполагают, что любая категориальная переменная не имеет значения, потому что местоположение не имеет значения. Мы будем рассматривать только «Реклама x образование» и «цена x возраст». Давайте запустим новую регрессию, включая обе эти комбинации. Сначала мы добавляем функции в наш исходный DataFrame, который содержал одномерные данные.

X_train_int = X_train_prep
X_train_int['ad_ed'] = X_train_int['Advertising'] * X_train_int['Age']

Теперь мы запускаем еще одну регрессию с нашими новыми функциями в DataFrame.

lr4 = LinearRegression()
lr4.fit(X_train_int, y_train)
lr4.score(X_train_int, y_train)
  • Оценка R-квадрат = 0,74

Мы видим, что наш показатель линейной регрессии увеличился с 0,73 до 0,74. Очевидно, что это минимальное увеличение, и оно не является суперсущественным, но, тем не менее, мы можем увидеть, как создание новых двумерных терминов характеристик может сыграть значительную роль в улучшении нашей модели.

Разработка функций - Полиномы

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

Для этого воспользуемся объектом PolynomialFeatures из библиотеки sklearn.preprocessing. Мы создаем новый пустой словарь для хранения возможностей наших новых функций (таких же, как двумерные). Затем мы переберем наш набор функций X_train_int и создадим новую функцию для каждой соответствующей функции, которая будет возведена в квадрат в пятой степени. Затем мы подгоним линейную регрессию к каждой из новых индивидуальных функций и выберем наиболее эффективную функцию для использования в нашей окончательной регрессии.

from sklearn.preprocessing import PolynomialFeatures
poly_dict = {}
for feature in X_train_int.columns:
   for p in range(2, 5):
      X_train_poly = X_train_int
      X_train_poly['sq'] = X_train_poly[feature] ** p
      lr = LinearRegression()
      lr.fit(X_train_poly, y_train)
      poly_dict[lr.score(X_train_poly, y_train) = [feature, p]
poly_dict[max(poly_dict.keys())]
  • Оценка R-квадрат = 0,743

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