Используйте Numpy и линейную алгебру для подбора нескольких моделей регрессии

Линейная регрессия — чрезвычайно популярная и полезная модель. Его используют как гуру Excel, так и специалисты по обработке и анализу данных, но как нам быстро сопоставить множество регрессионных моделей? В этой статье рассматриваются различные способы подбора модели линейной регрессии и способы ускорения процесса с помощью линейной алгебры.

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

  1. Подгонка модели с помощью библиотеки statsmodels
  2. Подбор простой линейной регрессии с использованием только Numpy
  3. Использование матричной формулировки
  4. Как подогнать несколько моделей за один проход по сгруппированным данным
  5. Сравнение производительности циклического и матричного подходов

Обзор данных

Данные, используемые в этой статье, будут сгенерированы для примера. Используя numpy +, вводящий некоторый случайный шум, приведенный ниже набор данных был создан для демонстрации методов подбора OLS. Эти данные будут храниться в переменных и для следующих нескольких примеров.

Статистические модели OLS

Одним из замечательных подходов к подбору линейной модели является использование библиотеки statsmodels. Мы просто передаем две переменные, вызываем метод fit и можем получить результаты с помощью вызова summary(). Примечание. Доступ к этим параметрам также можно получить напрямую с помощью fit.params.

Приведенные выше результаты показывают ряд деталей, но в этом пошаговом руководстве мы сосредоточимся на коэффициентах. Вы увидите в столбце ceof, что точка пересечения по оси y (помеченная как const) составляет ~211,62, а наш коэффициент регрессора x равен 101,65. Это хорошо согласуется с ожиданиями, учитывая параметры и случайность, введенные в исходный набор данных.

Арифметическое решение Numpy для простой линейной регрессии

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

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

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

Наконец, мы проверяем, чтобы результаты нашего решения numpy точно соответствовали версии statsmodels, используя функцию allclose.

Output:
Simple linear regression arithmetic slope of 101.65 vs. statsmodels ols fit slope of 101.65. 
Results equal: True

Матричная формула

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

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

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

Несколько регрессий одновременно

Примеры использования

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

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

Форма данных и формула множественной матрицы

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

Данные имеют форму — представляют 2 группы, по 50 точек данных в каждой и 2 независимые переменные для каждой (+ перехват). Теперь наша переменная Y — 2 группы, 50 точек данных.

Если мы попытаемся подключить это к нашей функции ols statsmodels, мы получим ошибку. Однако мы можем расширить нашу матричную формулу, чтобы найти точки пересечения/наклоны для каждой модели за один проход. Давайте математически определим, чего мы пытаемся достичь:

Для n регрессий (в данном случае 2) с каждой отдельной регрессионной группой данных, представленной k, мы хотим запустить матричную версию старой линейной регрессии, которую мы выполнили в предыдущем раздел. Вы увидите, что это та же формула, что и в предыдущем разделе, мы просто представляем выполнение нескольких итераций (от k = 1 до n), один набор матричных умножений для каждого группировка.

Настройки транспонирования Numpy

Реализация этого в numpy требует нескольких простых изменений. Если бы мы просто подключили наши переменные X и y непосредственно в предыдущую формулировку, измерения не выстроились бы так, как нам хотелось бы. Причина, по которой это происходит, заключается в том, что использование той же функции транспонирования (.T в numpy) изменит нашу форму с на . Не совсем то, что мы хотим…

В документации numpy matmul указывается, что если с любым аргументом передается более двух измерений, операция обрабатывается как стек матриц, находящихся в последних двух индексах. Это то поведение, которое мы ищем, поэтому первое измерение должно оставаться равным 2 (складывая наши группы), и мы просто хотим поменять местами 50 и 3.

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

В следующих примерах кода X теперь будет обозначаться как X_many и аналогично для y, чтобы отличать его от предыдущих примеров.

Output: (2, 3, 50)

Пересмотренные операции Numpy

Имея в виду это транспонированное знание, пришло время внести изменения, чтобы решить эту сгруппированную версию. Для удобства чтения код numpy разбит на две строки. Транспонирование изменено для работы с правильными размерами, и к y_many добавлена ​​дополнительная пустая ось только для того, чтобы выровнять операцию matmul.

Output:
array([[209.9240413375,  95.3254823487,  61.4157238175],
       [514.164936664 ,   8.1355322839,   4.3353162671]])

Мы возвращаем массив, который содержит нашу точку пересечения и коэффициент для каждой независимой переменной. Выполнение аналогичной (но зацикленной) операции с использованием статистических моделей и использованием numpy.allclose для проверки показывает, что коэффициенты одинаковы.

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

Обозначение Эйнсума

Мы также можем решить эту проблему с помощью функции einsum в numpy. Это использует соглашение суммирования Эйнштейна, чтобы указать, как выполняются операции. На высоком уровне мы определяем индексы для каждой матрицы в виде строки — если порядок меняется местами, это указывает на транспонирование, а умножение происходит по обычным буквам. Для получения дополнительной информации ознакомьтесь с документами numpy, поскольку они рассматривают несколько примеров.

Версия einsum была разбита на несколько шагов для удобства чтения. Внутри строки обозначения мы указываем транспонирование вместе с умножением матриц, которое необходимо выполнить. Это также позволяет нам обрабатывать фрагмент кода np.newaxis, вместо этого указывая выходные размеры.

Output:
[[209.9240413375  95.3254823487  61.4157238175]
 [514.164936664    8.1355322839   4.3353162671]]

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

Сравнение скорости

Сгруппировать/применить шаблон

Один из способов, которым я видел несколько регрессий, реализованных одновременно, — это использование фрейма данных pandas с методом groupby/apply. В этом методе мы определяем функцию, и pandas разделит набор данных на группы и применит эту функцию — возвращая и уменьшая результаты по мере необходимости.

Это вариант использования в нашей «групповой регрессии». Переключение наших массивов numpy в один фрейм данных, группировка по столбцу regression_number и применение пользовательской функции ols, которая соответствует и возвращает результаты, обеспечит те же коэффициенты (хотя панды отображаются с небольшим количеством десятичных знаков) .

Сравнение производительности

Теперь, когда у нас есть два разных способа выполнения OLS для нескольких групп, как они работают?

Для этого теста вместо 2 групп по 50 точек и 2 регрессоров мы расширимся до 100 групп и 3 регрессоров — форма (100, 50, 4) — как более реалистичный сценарий.

Выполнение теста timeit в нашей записной книжке с использованием волшебной команды %%timeit показывает, что подход pandas groupby/apply занимает 120 миллисекунд, а версия матрицы numpy — всего 0,151 миллисекунды. Матричное умножение стало почти в 800 раз быстрее!

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

Краткое содержание

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

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

Все примеры и файлы доступны на Github.

Первоначально опубликовано на https://datastud.dev.