Превью статьи
Искусственный интеллект
Машинное обучение
20 апреля
15 минут

Линейная регрессия — проще некуда

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

Вячеслав Гораш avatar

Вячеслав Гораш

Разработчик в области 3D графики и машинного обучения с 6-ти летним опытом

Это статья из цикла про основы машинного обучения.

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

Разбираемся с терминами

Для начала давайте определимся, что за задачу и как мы будем решать. Итак, решаем мы задачу регрессии. О том, что это за задача, мы говорили в прошлой статье, но на всякий случай напомню. У нас есть некие данные XX. Это может быть одно число или несколько (такой набор чисел называется вектором). Мы хотим для XX получить в качестве ответа одно значение, которое мы обозначаем y^\widehat{y}. Это значение непрерывно и может быть в любом диапазоне.

В качестве примера такой задачи можно привести определение веса человека по его росту. Тогда входными данными у нас будет одно число — рост человека в сантиметрах. На выходе мы тоже получим одно число — вес в килограммах. Как же нам посчитать это значение?

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

y=kx+by = kx + b

Эту формулу легко использовать и для нашего случая. Просто обозначим yy вес человека, а xx — рост. На самом деле нам еще нужно немного поправить обозначения. Обычно yy обозначают правильный ответ, то есть, в нашем случае, реальный вес человека. Мы же пытаемся сделать прогноз, и наш результат, как говорилось выше, будем обозначать не yy, а y^\widehat{y}. Но вернемся от обозначений к сути. Нам останется подобрать коэффициенты kk и bb, и мы сможем приблизительно решить задачу.

Но решение будет не очень точным (а скорее всего — очень неточным). Дело в том, что рост и вес не связаны линейной зависимостью напрямую. Есть высокие и худые люди, а есть невысокие и полные. И такая простая формула работать будет плохо. Чтобы увеличить точность нужно усложнять модель. Добавим обхват талии. Теперь наши входные данные уже не одно число xx, а вектор [x1,x2]\left\lbrack x_{1},x_{2} \right\rbrack: x1x_{1} обозначает рост, а x2x_{2} — обхват талии. Выходные данные остались прежними — вес в килограммах. Формула при этом меняется, но не радикально:

y^=k1x1+k2x2+b\widehat{y} = k_{1}x_{1} + k_{2}x_{2} + b

Для успешного обучения нам теперь нужно найти уже не два, а три параметра: k1k_{1}, k2k_{2} и bb. Для дальнейшего повышения точности мы можем и дальше добавлять входные данные, например, длину ноги, обхват груди и так далее. При этом каждое новое число во входных данных приносит с собой и новый параметр модели, который нам нужно будет найти. В общем виде, если у нас много параметров (обозначим это «много» как mm), формула будет иметь такой вид:

y^=k1x1+k2x2++kmxm+b\widehat{y} = k_{1}x_{1} + k_{2}x_{2} + \ldots + k_{m}x_{m} + b

или

y^=i=1mkixi+b\widehat{y} = \sum_{i = 1}^{m} k_{i}x_{i} + b

Все, что нам осталось — это разобраться, как найти параметры kik_{i} и bb.

Учимся обучаться

Для начала стоит вернуться к самому простому случаю, когда у нас один параметр. В таком виде формула выглядит так:

y^=kx+b\widehat{y} = kx + b

Как говорилось выше, обучение состоит в том, чтобы подобрать коэффициенты kk и bb. Какими должны быть эти коэффициенты? Мы хотим, чтобы наши ответы y^\widehat{y} были максимально близки к правильным ответам yy. Что значит «максимально близки»? Разница между yy и y^\widehat{y} должна быть минимальной. Это можно записать как

yy^0\left| y - \widehat{y} \right| \rightarrow 0

С модулями работать неудобно, поэтому лучше вместо модуля использовать квадрат. Математически это эквивалентно (если модуль стремится к нулю, то и квадрат стремится к нулю тоже):

(yy^)20\left( y - \widehat{y} \right)^{2} \rightarrow 0

Если бы у нас был всего один пример, то задача бы решалась элементарно: выбираем любую пару kk и bb так, чтобы kx+b=ykx + b = y. Таких пар бесконечное множество, это следует из геометрических соображений, что через одну точку можно провести бесконечное число прямых. Но мы хотим, чтобы наша модель работала не для одной пары значений, а для всех возможных. То есть нам нужно провести линию так, чтобы она в среднем была максимально близко ко всем точкам:

i=1n(yiyi^)2n0\frac{ \sum_{i = 1}^{n} \left( y_{i} - \widehat{y_{i}} \right)^{2} }{n} \rightarrow 0

Знаменатель можем не учитывать, так как он ни на что не влияет. Теперь вспомним, как считается y^\widehat{y}:

i=1n(yi(kxi+b))20\sum_{i = 1}^{n}\left( y_{i} - \left( kx_{i} + b \right) \right)^{2} \rightarrow 0

Оптимизация такой функции называется методом наименьших квадратов.

Иллюстрация метода наименьших квадратов

На иллюстрации синими точками показаны реальные значения. Красными — предсказания модели. Красная линия — график функции y=kx+by = kx + b. Мы стараемся минимизировать сумму расстояний между красными и синими точками (пунктирные линии).

Оптимизируем

Давайте посмотрим на нашу функцию:

i=1n(yi(kxi+b))2\sum_{i = 1}^{n}\left( y_{i} - \left( kx_{i} + b \right) \right)^{2}

От чего она зависит? Входные данные xix_{i} и правильные ответы yiy_{i} у нас неизменны. Значит, сумма будет зависеть только от параметров kk и bb. Получается, что у нас функция от двух переменных, и мы должны найти ее минимум. Если нарисовать поверхность, которую описывает эта функция, мы получим такую картинку:

Поверхность, описываемая квадратичной функцией

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

Самое главное, что производные мы можем считать отдельно по каждой из переменных. Сначала считаем производную по bb. Знак суммы — это просто операция сложения. Вспоминаем цепное правило:

ddxf(g(x))=ddgf(g(x))ddxg(x)\frac{d}{dx} f(g(x)) = \frac{d}{dg} f(g(x)) \cdot \frac{d}{dx} g(x)

Считаем сначала по bb:

ddbi=1n(yi(kxi+b))2=2i=1n(yi(kxi+b))\frac{d}{db}\sum_{i = 1}^{n}\left( y_{i} - \left( kx_{i} + b \right) \right)^{2} = - 2\sum_{i = 1}^{n}\left( y_{i} - \left( kx_{i} + b \right) \right)

Уберем минус перед суммой, просто поменяв местами выражения в скобках:

2i=1n(yi(kxi+b))=2i=1n((kxi+b)yi)-2\sum_{i = 1}^{n}\left( y_{i} - \left( kx_{i} + b \right) \right) = 2\sum_{i = 1}^{n}\left( \left( kx_{i} + b \right) - y_{i} \right)

Так как у нас теперь нет квадрата, мы можем разложить выражение на несколько сумм:

2i=1n((kxi+b)yi)=2i=1nkxi+2i=1nb2i=1nyi2\sum_{i = 1}^{n}\left( \left( kx_{i} + b \right) - y_{i} \right) = 2\sum_{i = 1}^{n}kx_{i} + 2\sum_{i = 1}^{n} b-2 \sum_{i = 1}^{n} y_{i}

Обратите внимание, что 2i=1nb2\sum_{i = 1}^{n}b — это просто сложение bb с самим собой 2n2n раз, то есть 2nb2nb.

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

2i=1n(kxi)+2nb2i=1nyi=02\sum_{i = 1}^{n}\left( kx_{i} \right) + 2nb - 2\sum_{i = 1}^{n}{y_{i} = 0}

Сократим двойки и перенесем элементы, чтобы выразить bb:

i=1n(kxi)+nbi=1nyi=0\sum_{i = 1}^{n}\left( kx_{i} \right) + nb - \sum_{i = 1}^{n}{y_{i} = 0}nb=i=1nyii=1nkxinb = \sum_{i = 1}^{n} y_{i} - \sum_{i = 1}^{n} kx_{i}b=i=1nyiki=1nxinb = \frac{\sum_{i = 1}^{n} y_{i} - k\sum_{i = 1}^{n} x_{i} }{n}

Теперь так же посчитаем производную по kk:

ddki=1n(yi(kxi+b))2=2i=1n((yi(kxi+b))xi)\frac{d}{dk} \sum_{i = 1}^{n} \left( y_{i} - \left( kx_{i} + b \right) \right)^{2} = - 2\sum_{i = 1}^{n}\left( \left( y_{i} - \left( kx_{i} + b \right) \right)x_{i} \right)

Переставляем элементы, чтобы убрать минус:

2i=1n((yi(kxi+b))xi)=2i=1n((kxi+byi)xi)-2\sum_{i = 1}^{n}\left( \left( y_{i} - \left( kx_{i} + b \right) \right) \cdot x_{i} \right) = 2\sum_{i = 1}^{n}{\left( \left( kx_{i} + b - y_{i} \right)x_{i} \right)}

Приравниваем к нулю и сокращаем двойку:

2i=1n((kxi+byi)xi)=02\sum_{i = 1}^{n} \left( \left( kx_{i} + b - y_{i} \right) \cdot x_{i} \right) = 0i=1n((kxi+byi)xi)=0\sum_{i = 1}^{n}\left( \left( kx_{i} + b - y_{i} \right) \cdot x_{i} \right) = 0

В итоге мы получаем систему из двух уравнений:

{i=1n((kxi+byi)xi)=0b=i=1nyiki=1nxin\begin{cases} \sum_{i = 1}^{n} \left( \left(k * x_{i} + b - y_{i}\right) \cdot x_{i} \right) = 0 \\ b = \frac{ \sum_{i = 1}^{n} y_{i} - k \sum_{i = 1}^{n} x_{i} }{n} \end{cases}

Эту систему можно немного упростить. Давайте посмотрим на второе уравнение. Представим его как:

b=i=1nyinki=1nxinb = \frac{ \sum_{i = 1}^{n} y_{i} }{n} - k \sdot \frac{ \sum_{i = 1}^{n} x_{i} }{n}

Что такое i=1nyin\frac{ \sum_{i = 1}^{n} y_{i} }{n} ? Это среднее значение yy (потому что мы сумму всех элементов делим на количество). Обозначим его как y\overline{y}. Аналогично поступим с xx, там такое же среднее. В итоге получим:

b=ykxb = \overline{y} - k \overline{x}

Согласитесь: без всех этих сумм гораздо проще. Теперь поступим как в школе: подставим bb в первое уравнение.

i=1n((kxi+byi)xi)=0\sum_{i = 1}^{n} \left( \left( kx_{i} + b - y_{i} \right) \cdot x_{i} \right) = 0i=1n((kxi+ykxyi)xi)=0\sum_{i = 1}^{n} \left( \left( kx_{i} + \overline{y} - k\overline{x} - y_{i} \right) \cdot x_{i} \right) = 0

Сократим все, что можно, и раскроем скобки:

i=1n((k(xix)+yyi)xi)=0\sum_{i = 1}^{n} \left( \left( k{(x}_{i} - \overline{x}) + \overline{y} - y_{i} \right) \cdot x_{i} \right) = 0ki=1n((xix)xi)+i=1n((yyi)xi)=0k \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot x_{i} \right) + \sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \cdot x_{i} \right) = 0

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

ki=1n((xix)xi)=i=1n((yyi)xi)k \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot x_{i} \right) = - \sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \cdot x_{i} \right)k=i=1n((yyi)xi)i=1n((xix)xi)k = - \frac{ \sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \sdot x_{i} \right) }{ \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot x_{i} \right) }

Формулу расчета можно еще упростить. Как именно, я покажу в конце статьи, чтобы не оставлять здесь еще больше формул. В итоге мы получаем:

{k=i=1n(xix)(yiy)i=1n(xix)2b=ykx\begin{cases} k = \frac{ \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right) \left(y_{i} - \overline{y} \right) }{ \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2} } \\ b = \overline{y} - k\overline{x} \end{cases}

Таким образом, для любого набора xx и yy мы можем посчитать коэффициенты. Ведь kk не зависит от bb. Считаем его по формуле, а потом, уже зная kk, находим bb. И в итоге у нас есть первый рабочий алгоритм машинного обучения.

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

y^=kx+b\widehat{y} = kx + b

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

Вернемся к задаче определения веса человека по его росту. Допустим, у нас есть набор данных:

Рост (x), смВес (y), кг
15443
196107
17273
18580
16166

В первую очередь, найдем средние значения:

x=173.6\overline{x} = 173.6

y=73.8\overline{y} = 73.8

Теперь нам надо найти коэффициенты. Начинаем с kk. Для каждого элемента считаем:

x\mathbf{x}y\mathbf{y}xx\mathbf{x - \overline{x}}yy\mathbf{y - \overline{y}}
15443-19.6-30.8
19610722.433.2
17273-1.6-0.8
1858011.46.2
16166-12.6-7.8

Теперь у нас есть все, чтобы посчитать kk по формуле:

k=i=1n(xix)(yiy)i=1n(xix)2k = \frac{ \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right) \left( y_{i} - \overline{y} \right) }{ \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2} }

В итоге получим k=1517.61177.21.29k = \frac{1517.6}{1177.2} \approx 1.29

Теперь считаем bb:

b=ykx=73.81.29173.6=150.144b = \overline{y} - k \cdot \overline{x} = 73.8 - 1.29 \cdot 173.6 = - 150.144

Теперь, когда у нас есть все коэффициенты, мы можем предсказывать вес для любого роста. Пусть у нас будет баскетболист с ростом 210 сантиметров. Попробуем предсказать его вес:

2101.29150.144120.75210 \cdot 1.29 - 150.144 \approx 120.75

Выглядит вполне правдоподобно.

Обобщаем

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

y^=k1x1+k2x2++kmxm+b\widehat{y} = k_{1}x_{1} + k_{2}x_{2} + \ldots + k_{m}x_{m} + b

Оптимизировать же мы будем функцию

i=1n(yi(k1x1i+k2x2i++kmxmi+b))2\sum_{i = 1}^{n} \left( y_{i} - \left( k_{1}x_{1i} + k_{2}x_{2i} + \ldots + k_{m}*x_{mi} + b \right) \right)^{2}

Сама оптимизация делается по тому же принципу. В итоге получается система из mm производных по каждому коэффициенту kik_{i}, одну производную по bb. В итоге мы получим систему из m+1m + 1 уравнения с m+1m + 1 неизвестным.

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

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

Не все так радужно

Казалось бы, если обучение настолько быстрое и простое (всего лишь посчитать несколько формул), почему линейная регрессия не применяется повсеместно? Ответ тут лежит в слове «линейная». И сейчас попробуем разобраться почему.

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

Проблема с нелинейной зависимостью

На картинке выше мы видим нелинейное распределение реальных данных (синие точки). Модель обучилась, постаравшись минимизировать расстояния между реальными данными и предсказаниями (красные точки). Но хоть расстояние и минимально возможное для данного случая, предсказание модели в зависимости от области может как быть относительно точным (2-я и 4-я точки), так и очень неточным (3-я точка).

Варианты с двумя или более аргументами также подвержены такому ограничению (но только уже не прямой, а плоскости или гиперплоскости).

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

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

Вместо послесловия

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

Напомню исходное выражение:

ki=1n((xix)xi)+i=1n((yyi)xi)=0k \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot x_{i} \right) + \sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \cdot x_{i} \right) = 0

Будем рассматривать только его первое слагаемое. Добавим к xix_{i} среднее значение, и сразу вычтем его для компенсации:

ki=1n((xix)xi)=ki=1n((xix)(xi+xx))k \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot x_{i} \right) = k \sum_{i = 1}^{n} \left( \left(x_{i} - \overline{x} \right) \cdot \left(x_{i} + \overline{x} - \overline{x} \right) \right)

Раскроем скобки:

ki=1n((xix)(xi+xx))=ki=1n(xi2+xixxixxixx2+x2)k \sum_{i = 1}^{n} \left( \left( x_{i} - \overline{x} \right) \cdot \left(x_{i} + \overline{x} - \overline{x} \right) \right) = k \sum_{i = 1}^{n} \left( x_{i}^{2} + x_{i} \overline{x} - x_{i} \overline{x} - x_{i} \overline{x} - {\overline{x}}^{2} + {\overline{x}}^{2} \right)

Разделим сумму на две части:

ki=1n(xi2+xixxixxixx2+x2)=ki=1n(xi2xixxix+x2)+ki=1n(xixx2)k \sum_{i = 1}^{n} \left( x_{i}^{2} + x_{i} \overline{x} - x_{i} \overline{x} - x_{i} \overline{x} - {\overline{x}}^{2} + {\overline{x}}^{2} \right) = k \sum_{i = 1}^{n} \left( x_{i}^{2} - x_{i} \overline{x} - x_{i} \overline{x} + {\overline{x}}^{2} \right) + k \sum_{i = 1}^{n} \left( x_{i}\overline{x} - {\overline{x}}^{2} \right)

В первой части мы видим формулу квадрата разности:

ki=1n(xi2xixxix+x2)=ki=1n(xi22xix+x2)=ki=1n(xix)2k \sum_{i = 1}^{n} \left( x_{i}^{2} - x_{i} \overline{x} - x_{i}\overline{x} + {\overline{x}}^{2} \right) = k \sum_{i = 1}^{n} \left( x_{i}^{2} - 2 x_{i} \overline{x} + {\overline{x}}^{2} \right) = k \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2}

Осталось разобраться со второй частью. Выносим x\overline{x} за сумму:

ki=1n(xixx2)=kxi=1n(xix)k \sum_{i = 1}^{n} \left( x_{i}\overline{x} - {\overline{x}}^{2} \right) = k \overline{x}\sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)

А вот теперь тот самый трюк со статистикой. (xix)( x_{i} - \overline{x} ) — это отклонение от среднего. И самое интересное, что сумма таких отклонений всегда равна нулю (это следует из статистики):

i=1n(xix)=0\sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right) = 0

Таким образом эту часть выражения можно опустить.

Вернемся к исходному уравнению, и так же обработаем его вторую часть:

i=1n((yyi)xi)=i=1n((yyi)(xix+x))=i=1n(yyi)(xix)+xi=1n(yyi)\sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \cdot x_{i} \right) = \sum_{i = 1}^{n} \left( \left( \overline{y} - y_{i} \right) \cdot \left( x_{i} - \overline{x} + \overline{x} \right) \right) = \sum_{i = 1}^{n} \left( \overline{y} - y_{i} \right) \cdot \left( x_{i} - \overline{x} \right) + \overline{x} \sum_{i = 1}^{n} \left( \overline{y} - y_{i} \right)

Второе слагаемое так же обратится в ноль, и мы получаем итоговое уравнение:

ki=1n(xix)2+i=1n(yyi)(xix)=0k \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2} + \sum_{i = 1}^{n} \left( \overline{y} - y_{i} \right)\cdot \left( x_{i} - \overline{x} \right) = 0

Отсюда, перенеся второе слагаемое направо, поменяв в нем знак и поделив, получаем:

ki=1n(xix)2=i=1n(yiy)(xix)k \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2} = \sum_{i = 1}^{n} \left( y_{i} - \overline{y} \right) \cdot \left( x_{i} - \overline{x} \right)k=i=1n(yiy)(xix)i=1n(xix)2k = \frac{ \sum_{i = 1}^{n} \left( y_{i} - \overline{y} \right)*\left( x_{i} - \overline{x} \right) }{ \sum_{i = 1}^{n} \left( x_{i} - \overline{x} \right)^{2} }

Вопросы и ответы

Обсудить проект

Опишите вашу задачу, мы проведём исследование и ответим вам как можно скорее.

С радостью проконсультируем вас любым из доступных способов.

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